Merge "Add FrameworksInProcessTests to presubmit." into sc-dev
diff --git a/apct-tests/perftests/core/src/android/app/OWNERS b/apct-tests/perftests/core/src/android/app/OWNERS
new file mode 100644
index 0000000..4f168ce
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/app/OWNERS
@@ -0,0 +1,2 @@
+per-file Overlay* = file:/core/java/android/app/RESOURCES_OWNERS
+per-file Resources* = file:/core/java/android/app/RESOURCES_OWNERS
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 18856f7..82e967a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -16,33 +16,43 @@
 
 package com.android.server.job;
 
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.UserSwitchObserver;
 import android.app.job.JobInfo;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.UserInfo;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseIntArray;
+import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.util.StatLogger;
 import com.android.server.JobSchedulerBackgroundThread;
+import com.android.server.LocalServices;
 import com.android.server.job.controllers.JobStatus;
 import com.android.server.job.controllers.StateController;
+import com.android.server.pm.UserManagerInternal;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -65,13 +75,18 @@
     // Try to give higher priority types lower values.
     static final int WORK_TYPE_NONE = 0;
     static final int WORK_TYPE_TOP = 1 << 0;
-    static final int WORK_TYPE_BG = 1 << 1;
-    private static final int NUM_WORK_TYPES = 2;
+    static final int WORK_TYPE_EJ = 1 << 1;
+    static final int WORK_TYPE_BG = 1 << 2;
+    static final int WORK_TYPE_BGUSER = 1 << 3;
+    @VisibleForTesting
+    static final int NUM_WORK_TYPES = 4;
 
     @IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = {
             WORK_TYPE_NONE,
             WORK_TYPE_TOP,
-            WORK_TYPE_BG
+            WORK_TYPE_EJ,
+            WORK_TYPE_BG,
+            WORK_TYPE_BGUSER
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface WorkType {
@@ -94,49 +109,63 @@
 
     private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_ON =
             new WorkConfigLimitsPerMemoryTrimLevel(
-                    new WorkTypeConfig("screen_on_normal", 8,
+                    new WorkTypeConfig("screen_on_normal", 11,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 2)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_EJ, 3),
+                                    Pair.create(WORK_TYPE_BG, 2)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 6))),
-                    new WorkTypeConfig("screen_on_moderate", 8,
+                            List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
+                    ),
+                    new WorkTypeConfig("screen_on_moderate", 9,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
+                                    Pair.create(WORK_TYPE_BG, 2)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 4))),
-                    new WorkTypeConfig("screen_on_low", 5,
+                            List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
+                    ),
+                    new WorkTypeConfig("screen_on_low", 6,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1),
+                                    Pair.create(WORK_TYPE_BG, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 1))),
+                            List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+                    ),
                     new WorkTypeConfig("screen_on_critical", 5,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 1)))
+                            List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+                    )
             );
     private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF =
             new WorkConfigLimitsPerMemoryTrimLevel(
-                    new WorkTypeConfig("screen_off_normal", 10,
+                    new WorkTypeConfig("screen_off_normal", 13,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
+                                    Pair.create(WORK_TYPE_BG, 2)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 6))),
-                    new WorkTypeConfig("screen_off_moderate", 10,
+                            List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
+                    ),
+                    new WorkTypeConfig("screen_off_moderate", 13,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 3),
+                                    Pair.create(WORK_TYPE_BG, 2)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 4))),
-                    new WorkTypeConfig("screen_off_low", 5,
+                            List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
+                    ),
+                    new WorkTypeConfig("screen_off_low", 7,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
+                                    Pair.create(WORK_TYPE_BG, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 1))),
+                            List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+                    ),
                     new WorkTypeConfig("screen_off_critical", 5,
                             // defaultMin
-                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+                            List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
                             // defaultMax
-                            List.of(Pair.create(WORK_TYPE_BG, 1)))
+                            List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+                    )
             );
 
     /**
@@ -171,6 +200,10 @@
             "assignJobsToContexts",
             "refreshSystemState",
     });
+    @VisibleForTesting
+    GracePeriodObserver mGracePeriodObserver;
+    @VisibleForTesting
+    boolean mShouldRestrictBgUser;
 
     interface Stats {
         int ASSIGN_JOBS_TO_CONTEXTS = 0;
@@ -182,9 +215,13 @@
     JobConcurrencyManager(JobSchedulerService service) {
         mService = service;
         mLock = mService.mLock;
-        mContext = service.getContext();
+        mContext = service.getTestableContext();
 
         mHandler = JobSchedulerBackgroundThread.getHandler();
+
+        mGracePeriodObserver = new GracePeriodObserver(mContext);
+        mShouldRestrictBgUser = mContext.getResources().getBoolean(
+                R.bool.config_jobSchedulerRestrictBackgroundUser);
     }
 
     public void onSystemReady() {
@@ -193,10 +230,18 @@
         final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         mContext.registerReceiver(mReceiver, filter);
+        try {
+            ActivityManager.getService().registerUserSwitchObserver(mGracePeriodObserver, TAG);
+        } catch (RemoteException e) {
+        }
 
         onInteractiveStateChanged(mPowerManager.isInteractive());
     }
 
+    void onUserRemoved(int userId) {
+        mGracePeriodObserver.onUserRemoved(userId);
+    }
+
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -224,7 +269,7 @@
                 Slog.d(TAG, "Interactive: " + interactive);
             }
 
-            final long nowRealtime = JobSchedulerService.sElapsedRealtimeClock.millis();
+            final long nowRealtime = sElapsedRealtimeClock.millis();
             if (interactive) {
                 mLastScreenOnRealtime = nowRealtime;
                 mEffectiveInteractiveState = true;
@@ -261,7 +306,7 @@
             if (mLastScreenOnRealtime > mLastScreenOffRealtime) {
                 return;
             }
-            final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+            final long now = sElapsedRealtimeClock.millis();
             if ((mLastScreenOffRealtime + mScreenOffAdjustmentDelayMs) > now) {
                 return;
             }
@@ -723,6 +768,10 @@
             pw.print(mLastMemoryTrimLevel);
             pw.println();
 
+            pw.print("User Grace Period: ");
+            pw.print(mGracePeriodObserver.mGracePeriodExpiration);
+            pw.println();
+
             mStatLogger.dump(pw);
         } finally {
             pw.decreaseIndent();
@@ -748,15 +797,52 @@
         proto.end(token);
     }
 
+    /**
+     * Decides whether a job is from the current foreground user or the equivalent.
+     */
+    @VisibleForTesting
+    boolean shouldRunAsFgUserJob(JobStatus job) {
+        if (!mShouldRestrictBgUser) return true;
+        int userId = job.getSourceUserId();
+        UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
+        UserInfo userInfo = um.getUserInfo(userId);
+
+        // If the user has a parent user (e.g. a work profile of another user), the user should be
+        // treated equivalent as its parent user.
+        if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
+                && userInfo.profileGroupId != userId) {
+            userId = userInfo.profileGroupId;
+            userInfo = um.getUserInfo(userId);
+        }
+
+        int currentUser = LocalServices.getService(ActivityManagerInternal.class)
+                .getCurrentUserId();
+        // A user is treated as foreground user if any of the followings is true:
+        // 1. The user is current user
+        // 2. The user is primary user
+        // 3. The user's grace period has not expired
+        return currentUser == userId || userInfo.isPrimary()
+                || mGracePeriodObserver.isWithinGracePeriodForUser(userId);
+    }
+
     int getJobWorkTypes(@NonNull JobStatus js) {
         int classification = 0;
-        // TODO(171305774): create dedicated work type for EJ and FGS
-        if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP
-                || js.shouldTreatAsExpeditedJob()) {
-            classification |= WORK_TYPE_TOP;
+        // TODO: create dedicated work type for FGS
+        if (shouldRunAsFgUserJob(js)) {
+            if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
+                classification |= WORK_TYPE_TOP;
+            } else {
+                classification |= WORK_TYPE_BG;
+            }
+
+            if (js.shouldTreatAsExpeditedJob()) {
+                classification |= WORK_TYPE_EJ;
+            }
         } else {
-            classification |= WORK_TYPE_BG;
+            // TODO(171305774): create dedicated slots for EJs of bg user
+            classification |= WORK_TYPE_BGUSER;
         }
+
         return classification;
     }
 
@@ -765,9 +851,15 @@
         private static final String KEY_PREFIX_MAX_TOTAL =
                 CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_";
         private static final String KEY_PREFIX_MAX_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "max_top_";
+        private static final String KEY_PREFIX_MAX_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "max_ej_";
         private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_";
+        private static final String KEY_PREFIX_MAX_BGUSER =
+                CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_";
         private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_";
+        private static final String KEY_PREFIX_MIN_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "min_ej_";
         private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_";
+        private static final String KEY_PREFIX_MIN_BGUSER =
+                CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_";
         private final String mConfigIdentifier;
 
         private int mMaxTotal;
@@ -811,10 +903,18 @@
                     properties.getInt(KEY_PREFIX_MAX_TOP + mConfigIdentifier,
                             mDefaultMaxAllowedSlots.get(WORK_TYPE_TOP, mMaxTotal))));
             mMaxAllowedSlots.put(WORK_TYPE_TOP, maxTop);
+            final int maxEj = Math.max(1, Math.min(mMaxTotal,
+                    properties.getInt(KEY_PREFIX_MAX_EJ + mConfigIdentifier,
+                            mDefaultMaxAllowedSlots.get(WORK_TYPE_EJ, mMaxTotal))));
+            mMaxAllowedSlots.put(WORK_TYPE_EJ, maxEj);
             final int maxBg = Math.max(1, Math.min(mMaxTotal,
                     properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier,
                             mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal))));
             mMaxAllowedSlots.put(WORK_TYPE_BG, maxBg);
+            final int maxBgUser = Math.max(1, Math.min(mMaxTotal,
+                    properties.getInt(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
+                            mDefaultMaxAllowedSlots.get(WORK_TYPE_BGUSER, mMaxTotal))));
+            mMaxAllowedSlots.put(WORK_TYPE_BGUSER, maxBgUser);
 
             int remaining = mMaxTotal;
             mMinReservedSlots.clear();
@@ -824,11 +924,23 @@
                             mDefaultMinReservedSlots.get(WORK_TYPE_TOP))));
             mMinReservedSlots.put(WORK_TYPE_TOP, minTop);
             remaining -= minTop;
+            // Ensure ej is in the range [0, min(maxEj, remaining)]
+            final int minEj = Math.max(0, Math.min(Math.min(maxEj, remaining),
+                    properties.getInt(KEY_PREFIX_MIN_EJ + mConfigIdentifier,
+                            mDefaultMinReservedSlots.get(WORK_TYPE_EJ))));
+            mMinReservedSlots.put(WORK_TYPE_EJ, minEj);
+            remaining -= minEj;
             // Ensure bg is in the range [0, min(maxBg, remaining)]
             final int minBg = Math.max(0, Math.min(Math.min(maxBg, remaining),
                     properties.getInt(KEY_PREFIX_MIN_BG + mConfigIdentifier,
                             mDefaultMinReservedSlots.get(WORK_TYPE_BG))));
             mMinReservedSlots.put(WORK_TYPE_BG, minBg);
+            remaining -= minBg;
+            // Ensure bg user is in the range [0, min(maxBgUser, remaining)]
+            final int minBgUser = Math.max(0, Math.min(Math.min(maxBgUser, remaining),
+                    properties.getInt(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
+                            mDefaultMinReservedSlots.get(WORK_TYPE_BGUSER, 0))));
+            mMinReservedSlots.put(WORK_TYPE_BGUSER, minBgUser);
         }
 
         int getMaxTotal() {
@@ -849,10 +961,18 @@
                     .println();
             pw.print(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_TOP))
                     .println();
+            pw.print(KEY_PREFIX_MIN_EJ + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_EJ))
+                    .println();
+            pw.print(KEY_PREFIX_MAX_EJ + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_EJ))
+                    .println();
             pw.print(KEY_PREFIX_MIN_BG + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_BG))
                     .println();
             pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG))
                     .println();
+            pw.print(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
+                    mMinReservedSlots.get(WORK_TYPE_BGUSER)).println();
+            pw.print(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
+                    mMaxAllowedSlots.get(WORK_TYPE_BGUSER)).println();
         }
     }
 
@@ -873,6 +993,58 @@
     }
 
     /**
+     * This class keeps the track of when a user's grace period expires.
+     */
+    @VisibleForTesting
+    static class GracePeriodObserver extends UserSwitchObserver {
+        // Key is UserId and Value is the time when grace period expires
+        @VisibleForTesting
+        final SparseLongArray mGracePeriodExpiration = new SparseLongArray();
+        private int mCurrentUserId;
+        @VisibleForTesting
+        int mGracePeriod;
+        private final UserManagerInternal mUserManagerInternal;
+        final Object mLock = new Object();
+
+
+        GracePeriodObserver(Context context) {
+            mCurrentUserId = LocalServices.getService(ActivityManagerInternal.class)
+                    .getCurrentUserId();
+            mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+            mGracePeriod = Math.max(0, context.getResources().getInteger(
+                    R.integer.config_jobSchedulerUserGracePeriod));
+        }
+
+        @Override
+        public void onUserSwitchComplete(int newUserId) {
+            final long expiration = sElapsedRealtimeClock.millis() + mGracePeriod;
+            synchronized (mLock) {
+                if (mCurrentUserId != UserHandle.USER_NULL
+                        && mUserManagerInternal.exists(mCurrentUserId)) {
+                    mGracePeriodExpiration.append(mCurrentUserId, expiration);
+                }
+                mGracePeriodExpiration.delete(newUserId);
+                mCurrentUserId = newUserId;
+            }
+        }
+
+        void onUserRemoved(int userId) {
+            synchronized (mLock) {
+                mGracePeriodExpiration.delete(userId);
+            }
+        }
+
+        @VisibleForTesting
+        public boolean isWithinGracePeriodForUser(int userId) {
+            synchronized (mLock) {
+                return userId == mCurrentUserId
+                        || sElapsedRealtimeClock.millis()
+                        < mGracePeriodExpiration.get(userId, Long.MAX_VALUE);
+            }
+        }
+    }
+
+    /**
      * This class decides, taking into account the current {@link WorkTypeConfig} and how many jobs
      * are running/pending, how many more job can start.
      *
@@ -892,20 +1064,21 @@
         private final SparseIntArray mNumPendingJobs = new SparseIntArray(NUM_WORK_TYPES);
         private final SparseIntArray mNumRunningJobs = new SparseIntArray(NUM_WORK_TYPES);
         private final SparseIntArray mNumStartingJobs = new SparseIntArray(NUM_WORK_TYPES);
-        private int mNumUnspecialized = 0;
         private int mNumUnspecializedRemaining = 0;
 
         void setConfig(@NonNull WorkTypeConfig workTypeConfig) {
             mConfigMaxTotal = workTypeConfig.getMaxTotal();
             mConfigNumReservedSlots.put(WORK_TYPE_TOP,
                     workTypeConfig.getMinReserved(WORK_TYPE_TOP));
+            mConfigNumReservedSlots.put(WORK_TYPE_EJ, workTypeConfig.getMinReserved(WORK_TYPE_EJ));
             mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG));
+            mConfigNumReservedSlots.put(WORK_TYPE_BGUSER,
+                    workTypeConfig.getMinReserved(WORK_TYPE_BGUSER));
             mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP));
+            mConfigAbsoluteMaxSlots.put(WORK_TYPE_EJ, workTypeConfig.getMax(WORK_TYPE_EJ));
             mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG));
+            mConfigAbsoluteMaxSlots.put(WORK_TYPE_BGUSER, workTypeConfig.getMax(WORK_TYPE_BGUSER));
 
-            mNumUnspecialized = mConfigMaxTotal;
-            mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP);
-            mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG);
             mNumUnspecializedRemaining = mConfigMaxTotal;
             for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {
                 mNumUnspecializedRemaining -= Math.max(mNumRunningJobs.valueAt(i),
@@ -934,9 +1107,15 @@
             if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) {
                 mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + 1);
             }
+            if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
+                mNumPendingJobs.put(WORK_TYPE_EJ, mNumPendingJobs.get(WORK_TYPE_EJ) + 1);
+            }
             if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
                 mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + 1);
             }
+            if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
+                mNumPendingJobs.put(WORK_TYPE_BGUSER, mNumPendingJobs.get(WORK_TYPE_BGUSER) + 1);
+            }
         }
 
         void stageJob(@WorkType int workType) {
@@ -1018,48 +1197,76 @@
 
         void onCountDone() {
             // Calculate how many slots to reserve for each work type. "Unspecialized" slots will
-            // be reserved for higher importance types first (ie. top before bg).
-            mNumUnspecialized = mConfigMaxTotal;
-            final int numTop = mNumRunningJobs.get(WORK_TYPE_TOP)
-                    + mNumPendingJobs.get(WORK_TYPE_TOP);
-            int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop);
-            mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop);
-            mNumUnspecialized -= resTop;
-            final int numBg = mNumRunningJobs.get(WORK_TYPE_BG) + mNumPendingJobs.get(WORK_TYPE_BG);
-            int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
-            mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg);
-            mNumUnspecialized -= resBg;
+            // be reserved for higher importance types first (ie. top before ej before bg).
+            // Steps:
+            //   1. Account for slots for already running jobs
+            //   2. Use remaining unaccounted slots to try and ensure minimum reserved slots
+            //   3. Allocate remaining up to max, based on importance
 
-            mNumUnspecializedRemaining = mNumUnspecialized;
-            // Account for already running jobs after we've assigned the minimum number of slots.
-            int unspecializedAssigned;
-            int extraRunning = (mNumRunningJobs.get(WORK_TYPE_TOP) - resTop);
-            if (extraRunning > 0) {
-                unspecializedAssigned = Math.max(0,
-                        Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP) - resTop,
-                                extraRunning));
-                resTop += unspecializedAssigned;
-                mNumUnspecializedRemaining -= extraRunning;
-            }
-            extraRunning = (mNumRunningJobs.get(WORK_TYPE_BG) - resBg);
-            if (extraRunning > 0) {
-                unspecializedAssigned = Math.max(0,
-                        Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG) - resBg, extraRunning));
-                resBg += unspecializedAssigned;
-                mNumUnspecializedRemaining -= extraRunning;
-            }
+            mNumUnspecializedRemaining = mConfigMaxTotal;
 
-            // Assign remaining unspecialized based on ranking.
-            unspecializedAssigned = Math.max(0,
+            // Step 1
+            int runTop = mNumRunningJobs.get(WORK_TYPE_TOP);
+            int resTop = runTop;
+            mNumUnspecializedRemaining -= resTop;
+            int runEj = mNumRunningJobs.get(WORK_TYPE_EJ);
+            int resEj = runEj;
+            mNumUnspecializedRemaining -= resEj;
+            int runBg = mNumRunningJobs.get(WORK_TYPE_BG);
+            int resBg = runBg;
+            mNumUnspecializedRemaining -= resBg;
+            int runBgUser = mNumRunningJobs.get(WORK_TYPE_BGUSER);
+            int resBgUser = runBgUser;
+            mNumUnspecializedRemaining -= resBgUser;
+
+            // Step 2
+            final int numTop = runTop + mNumPendingJobs.get(WORK_TYPE_TOP);
+            int fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+                    Math.min(numTop, mConfigNumReservedSlots.get(WORK_TYPE_TOP) - resTop)));
+            resTop += fillUp;
+            mNumUnspecializedRemaining -= fillUp;
+            final int numEj = runEj + mNumPendingJobs.get(WORK_TYPE_EJ);
+            fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+                    Math.min(numEj, mConfigNumReservedSlots.get(WORK_TYPE_EJ) - resEj)));
+            resEj += fillUp;
+            mNumUnspecializedRemaining -= fillUp;
+            final int numBg = runBg + mNumPendingJobs.get(WORK_TYPE_BG);
+            fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+                    Math.min(numBg, mConfigNumReservedSlots.get(WORK_TYPE_BG) - resBg)));
+            resBg += fillUp;
+            mNumUnspecializedRemaining -= fillUp;
+            final int numBgUser = runBgUser + mNumPendingJobs.get(WORK_TYPE_BGUSER);
+            fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+                    Math.min(numBgUser,
+                            mConfigNumReservedSlots.get(WORK_TYPE_BGUSER) - resBgUser)));
+            resBgUser += fillUp;
+            mNumUnspecializedRemaining -= fillUp;
+
+            // Step 3
+            int unspecializedAssigned = Math.max(0,
                     Math.min(mNumUnspecializedRemaining,
                             Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), numTop) - resTop));
             mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned);
             mNumUnspecializedRemaining -= unspecializedAssigned;
+
+            unspecializedAssigned = Math.max(0,
+                    Math.min(mNumUnspecializedRemaining,
+                            Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ), numEj) - resEj));
+            mNumActuallyReservedSlots.put(WORK_TYPE_EJ, resEj + unspecializedAssigned);
+            mNumUnspecializedRemaining -= unspecializedAssigned;
+
             unspecializedAssigned = Math.max(0,
                     Math.min(mNumUnspecializedRemaining,
                             Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), numBg) - resBg));
             mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned);
             mNumUnspecializedRemaining -= unspecializedAssigned;
+
+            unspecializedAssigned = Math.max(0,
+                    Math.min(mNumUnspecializedRemaining,
+                            Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER), numBgUser)
+                                    - resBgUser));
+            mNumActuallyReservedSlots.put(WORK_TYPE_BGUSER, resBgUser + unspecializedAssigned);
+            mNumUnspecializedRemaining -= unspecializedAssigned;
         }
 
         int canJobStart(int workTypes) {
@@ -1072,6 +1279,15 @@
                     return WORK_TYPE_TOP;
                 }
             }
+            if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
+                final int maxAllowed = Math.min(
+                        mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ),
+                        mNumActuallyReservedSlots.get(WORK_TYPE_EJ) + mNumUnspecializedRemaining);
+                if (mNumRunningJobs.get(WORK_TYPE_EJ) + mNumStartingJobs.get(WORK_TYPE_EJ)
+                        < maxAllowed) {
+                    return WORK_TYPE_EJ;
+                }
+            }
             if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
                 final int maxAllowed = Math.min(
                         mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG),
@@ -1081,6 +1297,16 @@
                     return WORK_TYPE_BG;
                 }
             }
+            if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
+                final int maxAllowed = Math.min(
+                        mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER),
+                        mNumActuallyReservedSlots.get(WORK_TYPE_BGUSER)
+                                + mNumUnspecializedRemaining);
+                if (mNumRunningJobs.get(WORK_TYPE_BGUSER) + mNumStartingJobs.get(WORK_TYPE_BGUSER)
+                        < maxAllowed) {
+                    return WORK_TYPE_BGUSER;
+                }
+            }
             return WORK_TYPE_NONE;
         }
 
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 7ce867c..bfc153f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -743,6 +743,7 @@
                         mControllers.get(c).onUserRemovedLocked(userId);
                     }
                 }
+                mConcurrencyManager.onUserRemoved(userId);
             } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
                 // Has this package scheduled any jobs, such that we will take action
                 // if it were to be force-stopped?
diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt
index 8b9990f..67fa9bb 100644
--- a/apex/media/framework/api/current.txt
+++ b/apex/media/framework/api/current.txt
@@ -29,6 +29,7 @@
   }
 
   public class MediaCommunicationManager {
+    method @IntRange(from=1) public int getVersion();
   }
 
   public class MediaController2 implements java.lang.AutoCloseable {
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
index b8065ef..e686076 100644
--- a/apex/media/framework/java/android/media/MediaCommunicationManager.java
+++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java
@@ -15,6 +15,7 @@
  */
 package android.media;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemService;
 import android.content.Context;
@@ -30,6 +31,16 @@
 public class MediaCommunicationManager {
     private static final String TAG = "MediaCommunicationManager";
 
+    /**
+     * The manager version used from beginning.
+     */
+    private static final int VERSION_1 = 1;
+
+    /**
+     * Current manager version.
+     */
+    private static final int CURRENT_VERSION = VERSION_1;
+
     private final Context mContext;
     private final IMediaCommunicationService mService;
 
@@ -43,7 +54,14 @@
         mContext = context;
         mService = IMediaCommunicationService.Stub.asInterface(
                 MediaFrameworkInitializer.getMediaServiceManager()
-                .getMediaCommunicationServiceRegisterer()
-                .get());
+                        .getMediaCommunicationServiceRegisterer()
+                        .get());
+    }
+
+    /**
+     * Gets the version of this {@link MediaCommunicationManager}.
+     */
+    public @IntRange(from = 1) int getVersion() {
+        return CURRENT_VERSION;
     }
 }
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index a7396fa..be82b22 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -43,7 +43,7 @@
 
 #include <android-base/properties.h>
 
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 #include <ui/PixelFormat.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -65,6 +65,8 @@
 
 namespace android {
 
+using ui::DisplayMode;
+
 static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
 static const char PRODUCT_BOOTANIMATION_DARK_FILE[] = "/product/media/bootanimation-dark.zip";
 static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
@@ -345,14 +347,14 @@
                         continue;
                     }
 
-                    DisplayConfig displayConfig;
-                    const status_t error = SurfaceComposerClient::getActiveDisplayConfig(
-                        mBootAnimation->mDisplayToken, &displayConfig);
+                    DisplayMode displayMode;
+                    const status_t error = SurfaceComposerClient::getActiveDisplayMode(
+                        mBootAnimation->mDisplayToken, &displayMode);
                     if (error != NO_ERROR) {
-                        SLOGE("Can't get active display configuration.");
+                        SLOGE("Can't get active display mode.");
                     }
-                    mBootAnimation->resizeSurface(displayConfig.resolution.getWidth(),
-                        displayConfig.resolution.getHeight());
+                    mBootAnimation->resizeSurface(displayMode.resolution.getWidth(),
+                        displayMode.resolution.getHeight());
                 }
             }
         } while (numEvents > 0);
@@ -401,15 +403,15 @@
     if (mDisplayToken == nullptr)
         return NAME_NOT_FOUND;
 
-    DisplayConfig displayConfig;
+    DisplayMode displayMode;
     const status_t error =
-            SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &displayConfig);
+            SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &displayMode);
     if (error != NO_ERROR)
         return error;
 
     mMaxWidth = android::base::GetIntProperty("ro.surface_flinger.max_graphics_width", 0);
     mMaxHeight = android::base::GetIntProperty("ro.surface_flinger.max_graphics_height", 0);
-    ui::Size resolution = displayConfig.resolution;
+    ui::Size resolution = displayMode.resolution;
     resolution = limitSurfaceSize(resolution.width, resolution.height);
     // create the native surface
     sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist
index 43a8a87..da4b255 100644
--- a/config/preloaded-classes-denylist
+++ b/config/preloaded-classes-denylist
@@ -4,7 +4,6 @@
 android.os.NullVibrator
 android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
 android.widget.Magnifier
-com.android.server.BootReceiver$2
 gov.nist.core.net.DefaultNetworkLayer
 android.net.rtp.AudioGroup
 android.net.rtp.AudioStream
diff --git a/core/api/current.txt b/core/api/current.txt
index f5c67c1..838dd55 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1100,6 +1100,7 @@
     field public static final int presentationTheme = 16843712; // 0x10103c0
     field public static final int preserveLegacyExternalStorage = 16844308; // 0x1010614
     field public static final int previewImage = 16843482; // 0x10102da
+    field public static final int previewLayout = 16844327; // 0x1010627
     field public static final int primaryContentAlpha = 16844114; // 0x1010552
     field public static final int priority = 16842780; // 0x101001c
     field public static final int privateImeOptions = 16843299; // 0x1010223
@@ -8386,6 +8387,7 @@
     field public int minResizeWidth;
     field public int minWidth;
     field public int previewImage;
+    field @IdRes public int previewLayout;
     field public android.content.ComponentName provider;
     field public int resizeMode;
     field public int updatePeriodMillis;
@@ -15795,6 +15797,7 @@
     method @NonNull public static android.graphics.RenderEffect createColorFilterEffect(@NonNull android.graphics.ColorFilter);
     method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float);
     method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float, @NonNull android.graphics.RenderEffect);
+    method @NonNull public static android.graphics.RenderEffect createShaderEffect(@NonNull android.graphics.Shader);
   }
 
   public final class RenderNode {
@@ -20477,6 +20480,7 @@
     field public static final int QUALITY_480P = 4; // 0x4
     field public static final int QUALITY_4KDCI = 10; // 0xa
     field public static final int QUALITY_720P = 5; // 0x5
+    field public static final int QUALITY_8KUHD = 13; // 0xd
     field public static final int QUALITY_CIF = 3; // 0x3
     field public static final int QUALITY_HIGH = 1; // 0x1
     field public static final int QUALITY_HIGH_SPEED_1080P = 2004; // 0x7d4
@@ -20498,6 +20502,7 @@
     field public static final int QUALITY_TIME_LAPSE_480P = 1004; // 0x3ec
     field public static final int QUALITY_TIME_LAPSE_4KDCI = 1010; // 0x3f2
     field public static final int QUALITY_TIME_LAPSE_720P = 1005; // 0x3ed
+    field public static final int QUALITY_TIME_LAPSE_8KUHD = 1013; // 0x3f5
     field public static final int QUALITY_TIME_LAPSE_CIF = 1003; // 0x3eb
     field public static final int QUALITY_TIME_LAPSE_HIGH = 1001; // 0x3e9
     field public static final int QUALITY_TIME_LAPSE_LOW = 1000; // 0x3e8
@@ -42724,7 +42729,11 @@
   }
 
   public class ImsRcsManager {
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
     field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
   }
 
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index cf4be05..5bb3e05 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1990,6 +1990,7 @@
     field public static final int UUID_BYTES_128_BIT = 16; // 0x10
     field public static final int UUID_BYTES_16_BIT = 2; // 0x2
     field public static final int UUID_BYTES_32_BIT = 4; // 0x4
+    field @NonNull public static final android.os.ParcelUuid VOLUME_CONTROL;
   }
 
   public final class BufferConstraint implements android.os.Parcelable {
@@ -4666,6 +4667,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String);
     method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
+    method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerProviderRequestListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.Listener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
@@ -4674,6 +4676,7 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setProviderEnabledForUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean unregisterGnssBatchedLocationCallback(@NonNull android.location.BatchedLocationCallback);
+    method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void unregisterProviderRequestListener(@NonNull android.location.provider.ProviderRequest.Listener);
   }
 
   public final class LocationRequest implements android.os.Parcelable {
@@ -4823,6 +4826,10 @@
     method @NonNull public android.location.provider.ProviderRequest.Builder setWorkSource(@NonNull android.os.WorkSource);
   }
 
+  public static interface ProviderRequest.Listener {
+    method public void onProviderRequestChanged(@NonNull String, @NonNull android.location.provider.ProviderRequest);
+  }
+
 }
 
 package android.media {
@@ -4972,6 +4979,7 @@
     method public android.media.PlayerProxy getPlayerProxy();
     method public int getPlayerState();
     method public int getPlayerType();
+    method @IntRange(from=0) public int getSessionId();
     method public boolean isActive();
     field public static final int PLAYER_STATE_IDLE = 1; // 0x1
     field public static final int PLAYER_STATE_PAUSED = 3; // 0x3
@@ -12951,6 +12959,17 @@
     method @Deprecated public void onRegistering(int);
   }
 
+  public class ImsRcsManager {
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnAvailabilityChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.OnAvailabilityChangedListener) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int, int) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnAvailabilityChangedListener(@NonNull android.telephony.ims.ImsRcsManager.OnAvailabilityChangedListener);
+  }
+
+  public static interface ImsRcsManager.OnAvailabilityChangedListener {
+    method public void onAvailabilityChanged(int);
+  }
+
   public final class ImsReasonInfo implements android.os.Parcelable {
     field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
   }
@@ -13591,11 +13610,24 @@
     ctor public RcsFeature(@NonNull java.util.concurrent.Executor);
     method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
     method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
+    method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
     method public void onFeatureReady();
     method public void onFeatureRemoved();
+    method public boolean queryCapabilityConfiguration(int, int);
+    method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
     method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase);
   }
 
+  public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
+    ctor public RcsFeature.RcsImsCapabilities(int);
+    method public void addCapabilities(int);
+    method public boolean isCapable(int);
+    method public void removeCapabilities(int);
+    field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+    field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+    field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
+  }
+
 }
 
 package android.telephony.ims.stub {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index fb27f74..af5df76 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -7,3 +7,8 @@
     name: "IDropBoxManagerService.aidl",
     srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
 }
+
+filegroup {
+    name: "ITracingServiceProxy.aidl",
+    srcs: ["android/tracing/ITracingServiceProxy.aidl"],
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e5a04c9..0774ac1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5685,6 +5685,13 @@
         final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
                 amOverrideConfig, contextThemeWrapperOverrideConfig);
         mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);
+        final Resources res = activity.getResources();
+        if (res.hasOverrideDisplayAdjustments()) {
+            // If fixed rotation is applied while the activity is visible (e.g. PiP), the rotated
+            // configuration of activity may be sent later than the adjustments. In this case, the
+            // adjustments need to be updated for the consistency of display info.
+            res.getDisplayAdjustments().getConfiguration().updateFrom(finalOverrideConfig);
+        }
 
         activity.mConfigChangeFlags = 0;
         activity.mCurrentConfig = new Configuration(newConfig);
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index e6aa7a7..1ff64db 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -59,7 +59,7 @@
 per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS
 
 # ResourcesManager
-per-file ResourcesManager.java = rtmitchell@google.com, toddke@google.com
+per-file ResourcesManager.java = file:RESOURCES_OWNERS
 
 # VoiceInteraction
 per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS
diff --git a/core/java/android/app/RESOURCES_OWNERS b/core/java/android/app/RESOURCES_OWNERS
new file mode 100644
index 0000000..21c39a8
--- /dev/null
+++ b/core/java/android/app/RESOURCES_OWNERS
@@ -0,0 +1,2 @@
+rtmitchell@google.com
+toddke@google.com
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index e96e22c4..42214d0 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -247,12 +247,29 @@
      * A preview of what the AppWidget will look like after it's configured.
      * If not supplied, the AppWidget's icon will be used.
      *
-     * <p>This field corresponds to the <code>android:previewImage</code> attribute in
-     * the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+     * <p>This field corresponds to the <code>android:previewImage</code> attribute in the AppWidget
+     * meta-data file.
      */
     public int previewImage;
 
     /**
+     * The layout resource id of a preview of what the AppWidget will look like after it's
+     * configured.
+     *
+     * <p>Unlike previewImage, previewLayout can better showcase AppWidget in different locales,
+     * system themes, display sizes & density etc.
+     *
+     * <p>If supplied, this will take precedence over the previewImage on supported widget hosts.
+     * Otherwise, previewImage will be used.
+     *
+     * <p>This field corresponds to the <code>android:previewLayout</code> attribute in the
+     * AppWidget meta-data file.
+     */
+    @SuppressLint("MutableBareField")
+    @IdRes
+    public int previewLayout;
+
+    /**
      * The rules by which a widget can be resized. See {@link #RESIZE_NONE},
      * {@link #RESIZE_NONE}, {@link #RESIZE_HORIZONTAL},
      * {@link #RESIZE_VERTICAL}, {@link #RESIZE_BOTH}.
@@ -320,6 +337,7 @@
         this.label = in.readString();
         this.icon = in.readInt();
         this.previewImage = in.readInt();
+        this.previewLayout = in.readInt();
         this.autoAdvanceViewId = in.readInt();
         this.resizeMode = in.readInt();
         this.widgetCategory = in.readInt();
@@ -429,6 +447,7 @@
         out.writeString(this.label);
         out.writeInt(this.icon);
         out.writeInt(this.previewImage);
+        out.writeInt(this.previewLayout);
         out.writeInt(this.autoAdvanceViewId);
         out.writeInt(this.resizeMode);
         out.writeInt(this.widgetCategory);
@@ -453,6 +472,7 @@
         that.label = this.label;
         that.icon = this.icon;
         that.previewImage = this.previewImage;
+        that.previewLayout = this.previewLayout;
         that.autoAdvanceViewId = this.autoAdvanceViewId;
         that.resizeMode = this.resizeMode;
         that.widgetCategory = this.widgetCategory;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 7eda50e..ea7e5ea 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3198,6 +3198,61 @@
     }
 
     /**
+     * Register a callback to receive events whenever the bluetooth stack goes down and back up,
+     * e.g. in the event the bluetooth is turned off/on via settings.
+     *
+     * If the bluetooth stack is currently up, there will not be an initial callback call.
+     * You can use the return value as an indication of this being the case.
+     *
+     * Callbacks will be delivered on a binder thread.
+     *
+     * @return whether bluetooth is already up currently
+     *
+     * @hide
+     */
+    public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) {
+        return getBluetoothService(callback.mRemote) != null;
+    }
+
+    /**
+     * Unregister a callback registered via {@link #registerServiceLifecycleCallback}
+     *
+     * @hide
+     */
+    public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) {
+        removeServiceStateCallback(callback.mRemote);
+    }
+
+    /**
+     * A callback for {@link #registerServiceLifecycleCallback}
+     *
+     * @hide
+     */
+    public abstract static class ServiceLifecycleCallback {
+
+        /** Called when the bluetooth stack is up */
+        public abstract void onBluetoothServiceUp();
+
+        /** Called when the bluetooth stack is down */
+        public abstract void onBluetoothServiceDown();
+
+        IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() {
+            @Override
+            public void onBluetoothServiceUp(IBluetooth bluetoothService) {
+                ServiceLifecycleCallback.this.onBluetoothServiceUp();
+            }
+
+            @Override
+            public void onBluetoothServiceDown() {
+                ServiceLifecycleCallback.this.onBluetoothServiceDown();
+            }
+
+            @Override
+            public void onBrEdrDown() {}
+        };
+    }
+
+    /**
      * Starts a scan for Bluetooth LE devices.
      *
      * <p>Results of the scan are reported using the
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index c0736a6..d82cf19 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -167,6 +167,11 @@
     /** @hide */
     @NonNull
     @SystemApi
+    public static final ParcelUuid VOLUME_CONTROL =
+            ParcelUuid.fromString("00001844-0000-1000-8000-00805F9B34FB");
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid BASE_UUID =
             ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
 
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java
index 17bf11b..960a087 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/Association.java
@@ -38,7 +38,7 @@
     private final @NonNull String mDeviceMacAddress;
     private final @NonNull String mPackageName;
     private final @Nullable String mDeviceProfile;
-    private final boolean mKeepProfilePrivilegesWhenDeviceAway;
+    private final boolean mNotifyOnDeviceNearby;
 
     /** @hide */
     public int getUserId() {
@@ -47,7 +47,7 @@
 
 
 
-    // Code below generated by codegen v1.0.21.
+    // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -71,7 +71,7 @@
             @NonNull String deviceMacAddress,
             @NonNull String packageName,
             @Nullable String deviceProfile,
-            boolean keepProfilePrivilegesWhenDeviceAway) {
+            boolean notifyOnDeviceNearby) {
         this.mUserId = userId;
         com.android.internal.util.AnnotationValidations.validate(
                 UserIdInt.class, null, mUserId);
@@ -82,7 +82,7 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mPackageName);
         this.mDeviceProfile = deviceProfile;
-        this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
+        this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -103,8 +103,8 @@
     }
 
     @DataClass.Generated.Member
-    public boolean isKeepProfilePrivilegesWhenDeviceAway() {
-        return mKeepProfilePrivilegesWhenDeviceAway;
+    public boolean isNotifyOnDeviceNearby() {
+        return mNotifyOnDeviceNearby;
     }
 
     @Override
@@ -118,7 +118,7 @@
                 "deviceMacAddress = " + mDeviceMacAddress + ", " +
                 "packageName = " + mPackageName + ", " +
                 "deviceProfile = " + mDeviceProfile + ", " +
-                "keepProfilePrivilegesWhenDeviceAway = " + mKeepProfilePrivilegesWhenDeviceAway +
+                "notifyOnDeviceNearby = " + mNotifyOnDeviceNearby +
         " }";
     }
 
@@ -139,7 +139,7 @@
                 && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
                 && Objects.equals(mPackageName, that.mPackageName)
                 && Objects.equals(mDeviceProfile, that.mDeviceProfile)
-                && mKeepProfilePrivilegesWhenDeviceAway == that.mKeepProfilePrivilegesWhenDeviceAway;
+                && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby;
     }
 
     @Override
@@ -153,7 +153,7 @@
         _hash = 31 * _hash + Objects.hashCode(mDeviceMacAddress);
         _hash = 31 * _hash + Objects.hashCode(mPackageName);
         _hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
-        _hash = 31 * _hash + Boolean.hashCode(mKeepProfilePrivilegesWhenDeviceAway);
+        _hash = 31 * _hash + Boolean.hashCode(mNotifyOnDeviceNearby);
         return _hash;
     }
 
@@ -164,7 +164,7 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
-        if (mKeepProfilePrivilegesWhenDeviceAway) flg |= 0x10;
+        if (mNotifyOnDeviceNearby) flg |= 0x10;
         if (mDeviceProfile != null) flg |= 0x8;
         dest.writeByte(flg);
         dest.writeInt(mUserId);
@@ -185,7 +185,7 @@
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
         byte flg = in.readByte();
-        boolean keepProfilePrivilegesWhenDeviceAway = (flg & 0x10) != 0;
+        boolean notifyOnDeviceNearby = (flg & 0x10) != 0;
         int userId = in.readInt();
         String deviceMacAddress = in.readString();
         String packageName = in.readString();
@@ -201,7 +201,7 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mPackageName);
         this.mDeviceProfile = deviceProfile;
-        this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
+        this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -221,10 +221,10 @@
     };
 
     @DataClass.Generated(
-            time = 1606940835778L,
-            codegenVersion = "1.0.21",
+            time = 1610482674799L,
+            codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/companion/Association.java",
-            inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final  boolean mKeepProfilePrivilegesWhenDeviceAway\npublic  int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final  boolean mNotifyOnDeviceNearby\npublic  int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 9bae1ff..bbf421d 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -854,8 +854,8 @@
      *
      * @hide Requires signature permission.
      */
-    public void setTemporaryBrightness(float brightness) {
-        mGlobal.setTemporaryBrightness(brightness);
+    public void setTemporaryBrightness(int displayId, float brightness) {
+        mGlobal.setTemporaryBrightness(displayId, brightness);
     }
 
     /**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 77ae947..60fe582 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -636,13 +636,13 @@
      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
      * </p>
      *
-     * @param brightness The brightness value from 0 to 255.
+     * @param brightness The brightness value from 0.0f to 1.0f.
      *
      * @hide Requires signature permission.
      */
-    public void setTemporaryBrightness(float brightness) {
+    public void setTemporaryBrightness(int displayId, float brightness) {
         try {
-            mDm.setTemporaryBrightness(brightness);
+            mDm.setTemporaryBrightness(displayId, brightness);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index a9f78fa..ff8a720 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -116,7 +116,7 @@
     boolean isMinimalPostProcessingRequested(int displayId);
 
     // Temporarily sets the display brightness.
-    void setTemporaryBrightness(float brightness);
+    void setTemporaryBrightness(int displayId, float brightness);
 
     // Temporarily sets the auto brightness adjustment factor.
     void setTemporaryAutoBrightnessAdjustment(float adjustment);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index c69c47f..eaa38f3 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -26,6 +26,7 @@
 import android.hardware.input.IInputSensorEventListener;
 import android.hardware.input.InputSensorInfo;
 import android.os.IBinder;
+import android.os.IVibratorStateListener;
 import android.os.VibrationEffect;
 import android.view.InputDevice;
 import android.view.InputEvent;
@@ -92,6 +93,8 @@
     void cancelVibrate(int deviceId, IBinder token);
     int[] getVibratorIds(int deviceId);
     boolean isVibrating(int deviceId);
+    boolean registerVibratorStateListener(int deviceId, in IVibratorStateListener listener);
+    boolean unregisterVibratorStateListener(int deviceId, in IVibratorStateListener listener);
 
     // Input device battery query.
     int getBatteryStatus(int deviceId);
diff --git a/core/java/android/hardware/input/InputDeviceVibrator.java b/core/java/android/hardware/input/InputDeviceVibrator.java
index c60d6ce..f4d8a65 100644
--- a/core/java/android/hardware/input/InputDeviceVibrator.java
+++ b/core/java/android/hardware/input/InputDeviceVibrator.java
@@ -18,10 +18,18 @@
 
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
+import android.app.ActivityThread;
+import android.content.Context;
 import android.os.Binder;
+import android.os.IVibratorStateListener;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
 
 import java.util.concurrent.Executor;
 
@@ -29,12 +37,18 @@
  * Vibrator implementation that communicates with the input device vibrators.
  */
 final class InputDeviceVibrator extends Vibrator {
+    private static final String TAG = "InputDeviceVibrator";
+
     // mDeviceId represents InputDevice ID the vibrator belongs to
     private final int mDeviceId;
     private final int mVibratorId;
     private final Binder mToken;
     private final InputManager mInputManager;
 
+    @GuardedBy("mDelegates")
+    private final ArrayMap<OnVibratorStateChangedListener,
+            OnVibratorStateChangedListenerDelegate> mDelegates = new ArrayMap<>();
+
     InputDeviceVibrator(InputManager inputManager, int deviceId, int vibratorId) {
         mInputManager = inputManager;
         mDeviceId = deviceId;
@@ -42,6 +56,23 @@
         mToken = new Binder();
     }
 
+    private class OnVibratorStateChangedListenerDelegate extends
+            IVibratorStateListener.Stub {
+        private final Executor mExecutor;
+        private final OnVibratorStateChangedListener mListener;
+
+        OnVibratorStateChangedListenerDelegate(@NonNull OnVibratorStateChangedListener listener,
+                @NonNull Executor executor) {
+            mExecutor = executor;
+            mListener = listener;
+        }
+
+        @Override
+        public void onVibrating(boolean isVibrating) {
+            mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating));
+        }
+    }
+
     @Override
     public boolean hasVibrator() {
         return true;
@@ -52,25 +83,73 @@
         return mInputManager.isVibrating(mDeviceId);
     }
 
-    /* TODO: b/161634264 Support Vibrator listener API in input devices */
+    /**
+     * Adds a listener for vibrator state changes. Callbacks will be executed on the main thread.
+     * If the listener was previously added and not removed, this call will be ignored.
+     *
+     * @param listener listener to be added
+     */
     @Override
     public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
-        throw new UnsupportedOperationException(
-            "addVibratorStateListener not supported in InputDeviceVibrator");
+        Preconditions.checkNotNull(listener);
+        Context context = ActivityThread.currentApplication();
+        addVibratorStateListener(context.getMainExecutor(), listener);
     }
 
+    /**
+     * Adds a listener for vibrator state change. If the listener was previously added and not
+     * removed, this call will be ignored.
+     *
+     * @param listener Listener to be added.
+     * @param executor The {@link Executor} on which the listener's callbacks will be executed on.
+     */
     @Override
     public void addVibratorStateListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnVibratorStateChangedListener listener) {
-        throw new UnsupportedOperationException(
-            "addVibratorStateListener not supported in InputDeviceVibrator");
+        Preconditions.checkNotNull(listener);
+        Preconditions.checkNotNull(executor);
+
+        synchronized (mDelegates) {
+            // If listener is already registered, reject and return.
+            if (mDelegates.containsKey(listener)) {
+                Log.w(TAG, "Listener already registered.");
+                return;
+            }
+
+            final OnVibratorStateChangedListenerDelegate delegate =
+                    new OnVibratorStateChangedListenerDelegate(listener, executor);
+            if (!mInputManager.registerVibratorStateListener(mDeviceId, delegate)) {
+                Log.w(TAG, "Failed to register vibrate state listener");
+                return;
+            }
+            mDelegates.put(listener, delegate);
+
+        }
     }
 
+    /**
+     * Removes the listener for vibrator state changes. If the listener was not previously
+     * registered, this call will do nothing.
+     *
+     * @param listener Listener to be removed.
+     */
     @Override
     public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
-        throw new UnsupportedOperationException(
-            "removeVibratorStateListener not supported in InputDeviceVibrator");
+        Preconditions.checkNotNull(listener);
+
+        synchronized (mDelegates) {
+            // Check if the listener is registered, otherwise will return.
+            if (mDelegates.containsKey(listener)) {
+                final OnVibratorStateChangedListenerDelegate delegate = mDelegates.get(listener);
+
+                if (!mInputManager.unregisterVibratorStateListener(mDeviceId, delegate)) {
+                    Log.w(TAG, "Failed to unregister vibrate state listener");
+                    return;
+                }
+                mDelegates.remove(listener);
+            }
+        }
     }
 
     @Override
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 185c59d..8a01c66 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -35,6 +35,7 @@
 import android.os.CombinedVibrationEffect;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IVibratorStateListener;
 import android.os.InputEventInjectionSync;
 import android.os.Looper;
 import android.os.Message;
@@ -1484,6 +1485,32 @@
     }
 
     /**
+     * Register input device vibrator state listener
+     *
+     * @hide
+     */
+    public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+        try {
+            return mIm.registerVibratorStateListener(deviceId, listener);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregister input device vibrator state listener
+     *
+     * @hide
+     */
+    public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+        try {
+            return mIm.unregisterVibratorStateListener(deviceId, listener);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Gets a sensor manager service associated with an input device, always create a new instance.
      * @return The sensor manager, never null.
      * @hide
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 5d8122b..32b19a4 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -32,7 +32,7 @@
 
 /**
  * Network definition that includes strong identity. Analogous to combining
- * {@link NetworkInfo} and an IMSI.
+ * {@link NetworkCapabilities} and an IMSI.
  *
  * @hide
  */
@@ -160,7 +160,7 @@
      */
     public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state,
             boolean defaultNetwork, @NetworkType int subType) {
-        final int type = state.networkInfo.getType();
+        final int legacyType = state.legacyNetworkType;
 
         String subscriberId = null;
         String networkId = null;
@@ -171,7 +171,7 @@
 
         subscriberId = state.subscriberId;
 
-        if (type == TYPE_WIFI) {
+        if (legacyType == TYPE_WIFI) {
             if (state.networkCapabilities.getSsid() != null) {
                 networkId = state.networkCapabilities.getSsid();
                 if (networkId == null) {
@@ -184,7 +184,7 @@
             }
         }
 
-        return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
+        return new NetworkIdentity(legacyType, subType, subscriberId, networkId, roaming, metered,
                 defaultNetwork);
     }
 
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index e1ef8b5..e466d2e 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
@@ -41,6 +42,7 @@
     public final Network network;
     public final String subscriberId;
     public final String networkId;
+    public final int legacyNetworkType;
 
     private NetworkState() {
         networkInfo = null;
@@ -49,17 +51,35 @@
         network = null;
         subscriberId = null;
         networkId = null;
+        legacyNetworkType = 0;
     }
 
+    public NetworkState(int legacyNetworkType, @NonNull LinkProperties linkProperties,
+            @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
+            @Nullable String subscriberId, @Nullable String networkId) {
+        this(legacyNetworkType, new NetworkInfo(legacyNetworkType, 0, null, null), linkProperties,
+                networkCapabilities, network, subscriberId, networkId);
+    }
+
+    // Constructor that used internally in ConnectivityService mainline module.
     public NetworkState(@NonNull NetworkInfo networkInfo, @NonNull LinkProperties linkProperties,
             @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
             String subscriberId, String networkId) {
+        this(networkInfo.getType(), networkInfo, linkProperties,
+                networkCapabilities, network, subscriberId, networkId);
+    }
+
+    public NetworkState(int legacyNetworkType, @NonNull NetworkInfo networkInfo,
+            @NonNull LinkProperties linkProperties,
+            @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
+            String subscriberId, String networkId) {
         this.networkInfo = networkInfo;
         this.linkProperties = linkProperties;
         this.networkCapabilities = networkCapabilities;
         this.network = network;
         this.subscriberId = subscriberId;
         this.networkId = networkId;
+        this.legacyNetworkType = legacyNetworkType;
 
         // This object is an atomic view of a network, so the various components
         // should always agree on roaming state.
@@ -80,6 +100,7 @@
         network = in.readParcelable(null);
         subscriberId = in.readString();
         networkId = in.readString();
+        legacyNetworkType = in.readInt();
     }
 
     @Override
@@ -95,6 +116,7 @@
         out.writeParcelable(network, flags);
         out.writeString(subscriberId);
         out.writeString(networkId);
+        out.writeInt(legacyNetworkType);
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9603f4d..a29fcb1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13580,28 +13580,6 @@
         public static final String POWER_BUTTON_VERY_LONG_PRESS =
                 "power_button_very_long_press";
 
-
-        /**
-         * Keyguard should be on the left hand side of the screen, for wide screen layouts.
-         *
-         * @hide
-         */
-        public static final int ONE_HANDED_KEYGUARD_SIDE_LEFT = 0;
-
-        /**
-         * Keyguard should be on the right hand side of the screen, for wide screen layouts.
-         *
-         * @hide
-         */
-        public static final int ONE_HANDED_KEYGUARD_SIDE_RIGHT = 1;
-        /**
-         * In one handed mode, which side the keyguard should be on. Allowable values are one of
-         * the ONE_HANDED_KEYGUARD_SIDE_* constants.
-         *
-         * @hide
-         */
-        public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
-
         /**
          * Keys we no longer back up under the current schema, but want to continue to
          * process when restoring historical backup datasets.
diff --git a/core/java/android/rotationresolver/RotationResolverInternal.java b/core/java/android/rotationresolver/RotationResolverInternal.java
index db879a7..1f1a66c 100644
--- a/core/java/android/rotationresolver/RotationResolverInternal.java
+++ b/core/java/android/rotationresolver/RotationResolverInternal.java
@@ -46,7 +46,6 @@
      *                 error is captured. {@link RotationResolverCallbackInternal}
      * @param proposedRotation the screen rotation that is proposed by the system.
      * @param currentRotation the current screen rotation.
-     * @param packageName the package name of the current activity that is running in foreground.
      * @param timeoutMillis the timeout in millisecond for the query. If the query doesn't get
      *                      fulfilled within this amount of time. It will be discarded and the
      *                      callback will receive a failure result code {@link
@@ -55,8 +54,7 @@
      */
     public abstract void resolveRotation(@NonNull RotationResolverCallbackInternal callback,
             @Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation,
-            String packageName, @DurationMillisLong long timeoutMillis,
-            @NonNull CancellationSignal cancellationSignal);
+            @DurationMillisLong long timeoutMillis, @NonNull CancellationSignal cancellationSignal);
 
     /**
      * Internal interfaces for the rotation resolver callback.
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index c39b8c5..a79b197 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -130,6 +130,15 @@
     public static final int KM_TAG_ASSOCIATED_DATA = Tag.ASSOCIATED_DATA; // KM_BYTES | 1000;
     public static final int KM_TAG_NONCE = Tag.NONCE; // KM_BYTES | 1001;
     public static final int KM_TAG_MAC_LENGTH = Tag.MAC_LENGTH; // KM_UINT | 1003;
+    public static final int KM_TAG_RESET_SINCE_ID_ROTATION =
+            Tag.RESET_SINCE_ID_ROTATION;     // KM_BOOL | 1004
+    public static final int KM_TAG_CONFIRMATION_TOKEN = Tag.CONFIRMATION_TOKEN; // KM_BYTES | 1005;
+    public static final int KM_TAG_CERTIFICATE_SERIAL = Tag.CERTIFICATE_SERIAL; // KM_UINT | 1006;
+    public static final int KM_TAG_CERTIFICATE_SUBJECT = Tag.CERTIFICATE_SUBJECT; // KM_UINT | 1007;
+    public static final int KM_TAG_CERTIFICATE_NOT_BEFORE =
+            Tag.CERTIFICATE_NOT_BEFORE; // KM_DATE | 1008;
+    public static final int KM_TAG_CERTIFICATE_NOT_AFTER =
+            Tag.CERTIFICATE_NOT_AFTER; // KM_DATE | 1009;
 
     // Algorithm values.
     public static final int KM_ALGORITHM_RSA = Algorithm.RSA;
@@ -317,6 +326,10 @@
             ErrorCode.HARDWARE_TYPE_UNAVAILABLE; // -68;
     public static final int KM_ERROR_DEVICE_LOCKED =
             ErrorCode.DEVICE_LOCKED; // -72;
+    public static final int KM_ERROR_MISSING_NOT_BEFORE =
+            ErrorCode.MISSING_NOT_BEFORE; // -80;
+    public static final int KM_ERROR_MISSING_NOT_AFTER =
+            ErrorCode.MISSING_NOT_AFTER; // -80;
     public static final int KM_ERROR_UNIMPLEMENTED =
             ErrorCode.UNIMPLEMENTED; // -100;
     public static final int KM_ERROR_VERSION_MISMATCH =
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 44daeff..2b7cb1d 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -41,7 +41,7 @@
     void onListenerHintsChanged(int hints);
     void onInterruptionFilterChanged(int interruptionFilter);
 
-    // companion device managers only
+    // companion device managers and assistants only
     void onNotificationChannelModification(String pkgName, in UserHandle user, in NotificationChannel channel, int modificationType);
     void onNotificationChannelGroupModification(String pkgName, in UserHandle user, in NotificationChannelGroup group, int modificationType);
 
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 16b45c3..7f45b38 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -475,9 +475,8 @@
 
         mObjects.insertAt(0, dirs);
 
-        final int baseLength = mBase.length();
-        // Update from 0 characters to whatever the real text is
-        reflow(mBase, 0, 0, baseLength);
+        // Update from 0 characters to whatever the displayed text is
+        reflow(mBase, 0, 0, mDisplay.length());
 
         if (mBase instanceof Spannable) {
             if (mWatcher == null)
@@ -485,6 +484,7 @@
 
             // Strip out any watchers for other DynamicLayouts.
             final Spannable sp = (Spannable) mBase;
+            final int baseLength = mBase.length();
             final ChangeWatcher[] spans = sp.getSpans(0, baseLength, ChangeWatcher.class);
             for (int i = 0; i < spans.length; i++) {
                 sp.removeSpan(spans[i]);
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 2de7558..aef185c 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -36,6 +36,7 @@
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 
 /**
@@ -155,6 +156,32 @@
         }
     };
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        FontConfig that = (FontConfig) o;
+        return mLastModifiedTimeMillis == that.mLastModifiedTimeMillis
+                && mConfigVersion == that.mConfigVersion
+                && Objects.equals(mFamilies, that.mFamilies)
+                && Objects.equals(mAliases, that.mAliases);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mFamilies, mAliases, mLastModifiedTimeMillis, mConfigVersion);
+    }
+
+    @Override
+    public String toString() {
+        return "FontConfig{"
+                + "mFamilies=" + mFamilies
+                + ", mAliases=" + mAliases
+                + ", mLastModifiedTimeMillis=" + mLastModifiedTimeMillis
+                + ", mConfigVersion=" + mConfigVersion
+                + '}';
+    }
+
     /**
      * Represents single font entry in system font configuration.
      *
@@ -317,6 +344,37 @@
         public boolean isItalic() {
             return getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC;
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Font font = (Font) o;
+            return mIndex == font.mIndex
+                    && Objects.equals(mFile, font.mFile)
+                    && Objects.equals(mOriginalFile, font.mOriginalFile)
+                    && Objects.equals(mStyle, font.mStyle)
+                    && Objects.equals(mFontVariationSettings, font.mFontVariationSettings)
+                    && Objects.equals(mFontFamilyName, font.mFontFamilyName);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mFile, mOriginalFile, mStyle, mIndex, mFontVariationSettings,
+                    mFontFamilyName);
+        }
+
+        @Override
+        public String toString() {
+            return "Font{"
+                    + "mFile=" + mFile
+                    + ", mOriginalFile=" + mOriginalFile
+                    + ", mStyle=" + mStyle
+                    + ", mIndex=" + mIndex
+                    + ", mFontVariationSettings='" + mFontVariationSettings + '\''
+                    + ", mFontFamilyName='" + mFontFamilyName + '\''
+                    + '}';
+        }
     }
 
     /**
@@ -398,6 +456,30 @@
                 return new Alias[size];
             }
         };
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Alias alias = (Alias) o;
+            return mWeight == alias.mWeight
+                    && Objects.equals(mName, alias.mName)
+                    && Objects.equals(mOriginal, alias.mOriginal);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mName, mOriginal, mWeight);
+        }
+
+        @Override
+        public String toString() {
+            return "Alias{"
+                    + "mName='" + mName + '\''
+                    + ", mOriginal='" + mOriginal + '\''
+                    + ", mWeight=" + mWeight
+                    + '}';
+        }
     }
 
     /**
@@ -413,7 +495,7 @@
     public static final class FontFamily implements Parcelable {
         private final @NonNull List<Font> mFonts;
         private final @Nullable String mName;
-        private final @Nullable LocaleList mLocaleList;
+        private final @NonNull LocaleList mLocaleList;
         private final @Variant int mVariant;
 
         /** @hide */
@@ -454,7 +536,7 @@
          * @hide Only system server can create this instance and passed via IPC.
          */
         public FontFamily(@NonNull List<Font> fonts, @Nullable String name,
-                @Nullable LocaleList localeList, @Variant int variant) {
+                @NonNull LocaleList localeList, @Variant int variant) {
             mFonts = fonts;
             mName = name;
             mLocaleList = localeList;
@@ -493,8 +575,6 @@
          *
          * The locale list will be used for deciding which font family should be used in fallback
          * list.
-         *
-         * @return non-null if a locale list is available. Otherwise null.
          */
         public @NonNull LocaleList getLocaleList() {
             return mLocaleList;
@@ -559,5 +639,31 @@
         public @NonNull String getLanguages() {
             return mLocaleList.toLanguageTags();
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            FontFamily that = (FontFamily) o;
+            return mVariant == that.mVariant
+                    && Objects.equals(mFonts, that.mFonts)
+                    && Objects.equals(mName, that.mName)
+                    && Objects.equals(mLocaleList, that.mLocaleList);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mFonts, mName, mLocaleList, mVariant);
+        }
+
+        @Override
+        public String toString() {
+            return "FontFamily{"
+                    + "mFonts=" + mFonts
+                    + ", mName='" + mName + '\''
+                    + ", mLocaleList=" + mLocaleList
+                    + ", mVariant=" + mVariant
+                    + '}';
+        }
     }
 }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 85911ff..f99d430 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -977,6 +977,9 @@
                 calculateEllipsis(start, end, measured, widthStart,
                         ellipsisWidth, ellipsize, j,
                         textWidth, paint, forceEllipsis);
+            } else {
+                mLines[mColumns * j + ELLIPSIS_START] = 0;
+                mLines[mColumns * j + ELLIPSIS_COUNT] = 0;
             }
         }
 
diff --git a/core/java/android/tracing/ITracingServiceProxy.aidl b/core/java/android/tracing/ITracingServiceProxy.aidl
new file mode 100644
index 0000000..4520db3
--- /dev/null
+++ b/core/java/android/tracing/ITracingServiceProxy.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tracing;
+
+/**
+ * Binder interface for the TracingServiceProxy running in system_server.
+ *
+ * {@hide}
+ */
+interface ITracingServiceProxy
+{
+    /**
+     * Notifies system tracing app that a tracing session has ended. If a session is repurposed
+     * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
+     * there is no buffer available to dump.
+     */
+    oneway void notifyTraceSessionEnded(boolean sessionStolen);
+}
diff --git a/core/java/android/tracing/OWNERS b/core/java/android/tracing/OWNERS
new file mode 100644
index 0000000..f5de4eb
--- /dev/null
+++ b/core/java/android/tracing/OWNERS
@@ -0,0 +1,2 @@
+cfijalkovich@google.com
+carmenjackson@google.com
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 790773f..b229212 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -65,7 +65,7 @@
         DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
 
         DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
-        DEFAULT_FLAGS.put("settings_silky_home", "true");
+        DEFAULT_FLAGS.put("settings_silky_home", "false");
         DEFAULT_FLAGS.put("settings_contextual_home", "false");
         DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
     }
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index 5ac95d4..c0d8187 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -94,7 +94,8 @@
         synchronized (this) {
             if (!hasSession(sessionHandle)) {
                 Log.w(TAG,
-                        "onRangingOpened - received unexpected SessionHandle: " + sessionHandle);
+                        "onRangingOpenedFailed - received unexpected SessionHandle: "
+                                + sessionHandle);
                 return;
             }
 
@@ -124,7 +125,7 @@
             @RangingChangeReason int reason, PersistableBundle params) {
         synchronized (this) {
             if (!hasSession(sessionHandle)) {
-                Log.w(TAG, "onRangingStartFailed - received unexpected SessionHandle: "
+                Log.w(TAG, "onRangingReconfigureFailed - received unexpected SessionHandle: "
                         + sessionHandle);
                 return;
             }
diff --git a/core/java/android/uwb/SessionHandle.java b/core/java/android/uwb/SessionHandle.java
index 928fcbdc..b23f5ad 100644
--- a/core/java/android/uwb/SessionHandle.java
+++ b/core/java/android/uwb/SessionHandle.java
@@ -19,6 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * @hide
  */
@@ -73,6 +75,11 @@
     }
 
     @Override
+    public int hashCode() {
+        return Objects.hashCode(mId);
+    }
+
+    @Override
     public String toString() {
         return "SessionHandle [id=" + mId + "]";
     }
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 5d4a4e5..e6cd252 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -58,11 +58,11 @@
     public static final int VSYNC_SOURCE_SURFACE_FLINGER = 1;
 
     /**
-     * Specifies to generate config changed events from Surface Flinger.
+     * Specifies to generate mode changed events from Surface Flinger.
      * <p>
      * Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h
      */
-    public static final int EVENT_REGISTRATION_CONFIG_CHANGED_FLAG = 0x1;
+    public static final int EVENT_REGISTRATION_MODE_CHANGED_FLAG = 0x1;
 
     /**
      * Specifies to generate frame rate override events from Surface Flinger.
@@ -197,14 +197,14 @@
     }
 
     /**
-     * Called when a display config changed event is received.
+     * Called when a display mode changed event is received.
      *
      * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
      * timebase.
      * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
-     * @param configId The new config Id
+     * @param modeId The new mode Id
      */
-    public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+    public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
     }
 
     /**
@@ -273,8 +273,8 @@
 
     // Called from native code.
     @SuppressWarnings("unused")
-    private void dispatchConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
-        onConfigChanged(timestampNanos, physicalDisplayId, configId);
+    private void dispatchModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
+        onModeChanged(timestampNanos, physicalDisplayId, modeId);
     }
 
     // Called from native code.
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 24bc308..f8c4d15 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -928,7 +928,9 @@
      * seamless transition is one that doesn't have any visual interruptions, such as a black
      * screen for a second or two. True indicates that any frame rate changes caused by this
      * request should be seamless. False indicates that non-seamless refresh rates are also
-     * acceptable.
+     * acceptable. Non-seamless switches might be used when the benefit of matching the content's
+     * frame rate outweighs the cost of the transition, for example when displaying
+     * long-running video content.
      *
      * @throws IllegalArgumentException If frameRate or compatibility are invalid.
      */
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 98b4acd..6a629ca 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -161,8 +161,8 @@
             int L, int T, int R, int B);
     private static native void nativeSetDisplaySize(long transactionObj, IBinder displayToken,
             int width, int height);
-    private static native SurfaceControl.DisplayInfo nativeGetDisplayInfo(IBinder displayToken);
-    private static native SurfaceControl.DisplayConfig[] nativeGetDisplayConfigs(
+    private static native DisplayInfo nativeGetDisplayInfo(IBinder displayToken);
+    private static native DisplayMode[] nativeGetDisplayModes(
             IBinder displayToken);
     private static native DisplayedContentSamplingAttributes
             nativeGetDisplayedContentSamplingAttributes(IBinder displayToken);
@@ -170,13 +170,13 @@
             boolean enable, int componentMask, int maxFrames);
     private static native DisplayedContentSample nativeGetDisplayedContentSample(
             IBinder displayToken, long numFrames, long timestamp);
-    private static native int nativeGetActiveConfig(IBinder displayToken);
-    private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken,
-            SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs);
-    private static native SurfaceControl.DesiredDisplayConfigSpecs
-            nativeGetDesiredDisplayConfigSpecs(IBinder displayToken);
+    private static native int nativeGetActiveDisplayMode(IBinder displayToken);
+    private static native boolean nativeSetDesiredDisplayModeSpecs(IBinder displayToken,
+            DesiredDisplayModeSpecs desiredDisplayModeSpecs);
+    private static native DesiredDisplayModeSpecs
+            nativeGetDesiredDisplayModeSpecs(IBinder displayToken);
     private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
-    private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries(
+    private static native DisplayPrimaries nativeGetDisplayNativePrimaries(
             IBinder displayToken);
     private static native int[] nativeGetCompositionDataspaces();
     private static native int nativeGetActiveColorMode(IBinder displayToken);
@@ -1743,11 +1743,11 @@
      *
      * @hide
      */
-    public static final class DisplayConfig {
+    public static final class DisplayMode {
         /**
          * Invalid display config id.
          */
-        public static final int INVALID_DISPLAY_CONFIG_ID = -1;
+        public static final int INVALID_DISPLAY_MODE_ID = -1;
 
         public int width;
         public int height;
@@ -1764,7 +1764,7 @@
          * configs within the same group can be done seamlessly in most cases.
          * @see: android.hardware.graphics.composer@2.4::IComposerClient::Attribute::CONFIG_GROUP
          */
-        public int configGroup;
+        public int group;
 
         @Override
         public String toString() {
@@ -1775,7 +1775,7 @@
                     + ", refreshRate=" + refreshRate
                     + ", appVsyncOffsetNanos=" + appVsyncOffsetNanos
                     + ", presentationDeadlineNanos=" + presentationDeadlineNanos
-                    + ", configGroup=" + configGroup + "}";
+                    + ", group=" + group + "}";
         }
     }
 
@@ -1802,21 +1802,21 @@
     /**
      * @hide
      */
-    public static SurfaceControl.DisplayConfig[] getDisplayConfigs(IBinder displayToken) {
+    public static DisplayMode[] getDisplayModes(IBinder displayToken) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
         }
-        return nativeGetDisplayConfigs(displayToken);
+        return nativeGetDisplayModes(displayToken);
     }
 
     /**
      * @hide
      */
-    public static int getActiveConfig(IBinder displayToken) {
+    public static int getActiveDisplayMode(IBinder displayToken) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
         }
-        return nativeGetActiveConfig(displayToken);
+        return nativeGetActiveDisplayMode(displayToken);
     }
 
     /**
@@ -1863,8 +1863,8 @@
      *
      * @hide
      */
-    public static final class DesiredDisplayConfigSpecs {
-        public int defaultConfig;
+    public static final class DesiredDisplayModeSpecs {
+        public int defaultMode;
         /**
          * The primary refresh rate range represents display manager's general guidance on the
          * display configs surface flinger will consider when switching refresh rates. Unless
@@ -1889,16 +1889,16 @@
          */
         public boolean allowGroupSwitching;
 
-        public DesiredDisplayConfigSpecs() {}
+        public DesiredDisplayModeSpecs() {}
 
-        public DesiredDisplayConfigSpecs(DesiredDisplayConfigSpecs other) {
+        public DesiredDisplayModeSpecs(DesiredDisplayModeSpecs other) {
             copyFrom(other);
         }
 
-        public DesiredDisplayConfigSpecs(int defaultConfig, boolean allowGroupSwitching,
+        public DesiredDisplayModeSpecs(int defaultMode, boolean allowGroupSwitching,
                 float primaryRefreshRateMin, float primaryRefreshRateMax,
                 float appRequestRefreshRateMin, float appRequestRefreshRateMax) {
-            this.defaultConfig = defaultConfig;
+            this.defaultMode = defaultMode;
             this.allowGroupSwitching = allowGroupSwitching;
             this.primaryRefreshRateMin = primaryRefreshRateMin;
             this.primaryRefreshRateMax = primaryRefreshRateMax;
@@ -1908,14 +1908,14 @@
 
         @Override
         public boolean equals(@Nullable Object o) {
-            return o instanceof DesiredDisplayConfigSpecs && equals((DesiredDisplayConfigSpecs) o);
+            return o instanceof DesiredDisplayModeSpecs && equals((DesiredDisplayModeSpecs) o);
         }
 
         /**
          * Tests for equality.
          */
-        public boolean equals(DesiredDisplayConfigSpecs other) {
-            return other != null && defaultConfig == other.defaultConfig
+        public boolean equals(DesiredDisplayModeSpecs other) {
+            return other != null && defaultMode == other.defaultMode
                     && primaryRefreshRateMin == other.primaryRefreshRateMin
                     && primaryRefreshRateMax == other.primaryRefreshRateMax
                     && appRequestRefreshRateMin == other.appRequestRefreshRateMin
@@ -1930,8 +1930,8 @@
         /**
          * Copies the supplied object's values to this object.
          */
-        public void copyFrom(DesiredDisplayConfigSpecs other) {
-            defaultConfig = other.defaultConfig;
+        public void copyFrom(DesiredDisplayModeSpecs other) {
+            defaultMode = other.defaultMode;
             primaryRefreshRateMin = other.primaryRefreshRateMin;
             primaryRefreshRateMax = other.primaryRefreshRateMax;
             appRequestRefreshRateMin = other.appRequestRefreshRateMin;
@@ -1942,7 +1942,7 @@
         public String toString() {
             return String.format("defaultConfig=%d primaryRefreshRateRange=[%.0f %.0f]"
                             + " appRequestRefreshRateRange=[%.0f %.0f]",
-                    defaultConfig, primaryRefreshRateMin, primaryRefreshRateMax,
+                    defaultMode, primaryRefreshRateMin, primaryRefreshRateMax,
                     appRequestRefreshRateMin, appRequestRefreshRateMax);
         }
     }
@@ -1950,25 +1950,31 @@
     /**
      * @hide
      */
-    public static boolean setDesiredDisplayConfigSpecs(IBinder displayToken,
-            SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs) {
+    public static boolean setDesiredDisplayModeSpecs(IBinder displayToken,
+            DesiredDisplayModeSpecs desiredDisplayModeSpecs) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
         }
+        if (desiredDisplayModeSpecs == null) {
+            throw new IllegalArgumentException("desiredDisplayModeSpecs must not be null");
+        }
+        if (desiredDisplayModeSpecs.defaultMode < 0) {
+            throw new IllegalArgumentException("defaultMode must be non-negative");
+        }
 
-        return nativeSetDesiredDisplayConfigSpecs(displayToken, desiredDisplayConfigSpecs);
+        return nativeSetDesiredDisplayModeSpecs(displayToken, desiredDisplayModeSpecs);
     }
 
     /**
      * @hide
      */
-    public static SurfaceControl.DesiredDisplayConfigSpecs getDesiredDisplayConfigSpecs(
+    public static DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(
             IBinder displayToken) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
         }
 
-        return nativeGetDesiredDisplayConfigSpecs(displayToken);
+        return nativeGetDesiredDisplayModeSpecs(displayToken);
     }
 
     /**
@@ -2039,7 +2045,7 @@
     /**
      * @hide
      */
-    public static SurfaceControl.DisplayPrimaries getDisplayNativePrimaries(
+    public static DisplayPrimaries getDisplayNativePrimaries(
             IBinder displayToken) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
@@ -3241,7 +3247,10 @@
          *                         interruptions, such as a black screen for a second or two. True
          *                         indicates that any frame rate changes caused by this request
          *                         should be seamless. False indicates that non-seamless refresh
-         *                         rates are also acceptable.
+         *                         rates are also acceptable. Non-seamless switches might be
+         *                         used when the benefit of matching the content's frame rate
+         *                         outweighs the cost of the transition, for example when
+         *                         displaying long-running video content.
          * @return This transaction object.
          */
         @NonNull
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index f019648..18029af 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -162,7 +162,6 @@
             @NonNull WindowlessWindowManager wwm, boolean useSfChoreographer) {
         mWm = wwm;
         mViewRoot = new ViewRootImpl(c, d, mWm, useSfChoreographer);
-        mViewRoot.forceDisableBLAST();
         mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
 
@@ -188,7 +187,6 @@
         mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
                 mSurfaceControl, hostToken);
         mViewRoot = new ViewRootImpl(context, display, mWm);
-        mViewRoot.forceDisableBLAST();
         mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
 
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index b10370a..acbcbfa 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -85,12 +85,13 @@
         for (int i = params.length - 1; i >= 0; i--) {
             SurfaceParams surfaceParams = params[i];
             SurfaceControl surface = surfaceParams.surface;
-            if (frame > 0) {
-                t.deferTransactionUntil(surface, mTargetSc, frame);
-            }
             applyParams(t, surfaceParams, mTmpFloat9);
         }
-        t.apply();
+        if (mTargetViewRootImpl != null) {
+            mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
+        } else {
+            t.apply();
+        }
     }
 
     public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 036a703..56d98e7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -10137,9 +10137,11 @@
      * Merges the transaction passed in with the next transaction in BLASTBufferQueue. This ensures
      * you can add transactions to the upcoming frame.
      */
-    void mergeWithNextTransaction(Transaction t, long frameNumber) {
+    public void mergeWithNextTransaction(Transaction t, long frameNumber) {
         if (mBlastBufferQueue != null) {
             mBlastBufferQueue.mergeWithNextTransaction(t, frameNumber);
+        } else {
+            t.apply();
         }
     }
 }
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index b85f1079..3aedda1 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -71,7 +71,7 @@
         new HashMap<IBinder, ResizeCompleteCallback>();
 
     private final SurfaceSession mSurfaceSession = new SurfaceSession();
-    private final SurfaceControl mRootSurface;
+    protected final SurfaceControl mRootSurface;
     private final Configuration mConfiguration;
     private final IWindowSession mRealWm;
     private final IBinder mHostInputToken;
@@ -126,7 +126,7 @@
         }
     }
 
-    protected void attachToParentSurface(SurfaceControl.Builder b) {
+    protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
         b.setParent(mRootSurface);
     }
 
@@ -140,10 +140,10 @@
             InsetsSourceControl[] outActiveControls) {
         final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
                 .setFormat(attrs.format)
-                .setBufferSize(getSurfaceWidth(attrs), getSurfaceHeight(attrs))
+                .setBLASTLayer()
                 .setName(attrs.getTitle().toString())
                 .setCallsite("WindowlessWindowManager.addToDisplay");
-        attachToParentSurface(b);
+        attachToParentSurface(window, b);
         final SurfaceControl sc = b.build();
 
         if (((attrs.inputFeatures &
@@ -162,7 +162,11 @@
             mStateForWindow.put(window.asBinder(), state);
         }
 
-        return WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
+        final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE |
+                        WindowManagerGlobal.ADD_FLAG_USE_BLAST;
+
+        // Include whether the window is in touch mode.
+        return isInTouchMode() ? res | WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE : res;
     }
 
     /**
@@ -210,6 +214,26 @@
         return !PixelFormat.formatHasAlpha(attrs.format);
     }
 
+    private boolean isInTouchMode() {
+        try {
+            return WindowManagerGlobal.getWindowSession().getInTouchMode();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to check if the window is in touch mode", e);
+        }
+        return false;
+    }
+
+    /** Access to package members for SystemWindow leashing
+     * @hide
+     */
+    protected IBinder getWindowBinder(View rootView) {
+        final ViewRootImpl root = rootView.getViewRootImpl();
+        if (root == null) {
+            return null;
+        }
+        return root.mWindow.asBinder();
+    }
+
     /** @hide */
     @Nullable
     protected SurfaceControl getSurfaceControl(View rootView) {
@@ -254,8 +278,8 @@
         WindowManager.LayoutParams attrs = state.mParams;
 
         if (viewFlags == View.VISIBLE) {
-            t.setBufferSize(sc, getSurfaceWidth(attrs), getSurfaceHeight(attrs))
-                    .setOpaque(sc, isOpaque(attrs)).show(sc).apply();
+            outSurfaceSize.set(getSurfaceWidth(attrs), getSurfaceHeight(attrs));
+            t.setOpaque(sc, isOpaque(attrs)).show(sc).apply();
             outSurfaceControl.copyFrom(sc, "WindowlessWindowManager.relayout");
         } else {
             t.hide(sc).apply();
@@ -276,7 +300,8 @@
             }
         }
 
-        return 0;
+        // Include whether the window is in touch mode.
+        return isInTouchMode() ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
     }
 
     @Override
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 6300320..0ab4e05 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -19,6 +19,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityThread;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.LocaleList;
@@ -69,7 +73,8 @@
 
     /**
      * The IME provided locales for the request. If non-empty, the inline suggestions should
-     * return languages from the supported locales. If not provided, it'll default to system locale.
+     * return languages from the supported locales. If not provided, it'll default to be empty if
+     * target SDK is S or above, and default to system locale otherwise.
      *
      * <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
      * to have one locale to guarantee consistent UI rendering.</p>
@@ -156,7 +161,18 @@
         return ActivityThread.currentPackageName();
     }
 
+    /**
+     * The {@link InlineSuggestionsRequest#getSupportedLocales()} now returns empty locale list when
+     * it's not set, instead of the default system locale.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY = 169273070L;
+
     private static LocaleList defaultSupportedLocales() {
+        if (CompatChanges.isChangeEnabled(IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY)) {
+            return LocaleList.getEmptyLocaleList();
+        }
         return LocaleList.getDefault();
     }
 
@@ -189,7 +205,7 @@
 
 
 
-    // Code below generated by codegen v1.0.15.
+    // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -264,7 +280,8 @@
 
     /**
      * The IME provided locales for the request. If non-empty, the inline suggestions should
-     * return languages from the supported locales. If not provided, it'll default to system locale.
+     * return languages from the supported locales. If not provided, it'll default to be empty if
+     * target SDK is S or above, and default to system locale otherwise.
      *
      * <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
      * to have one locale to guarantee consistent UI rendering.</p>
@@ -522,7 +539,8 @@
 
         /**
          * The IME provided locales for the request. If non-empty, the inline suggestions should
-         * return languages from the supported locales. If not provided, it'll default to system locale.
+         * return languages from the supported locales. If not provided, it'll default to be empty if
+         * target SDK is S or above, and default to system locale otherwise.
          *
          * <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
          * to have one locale to guarantee consistent UI rendering.</p>
@@ -622,10 +640,10 @@
     }
 
     @DataClass.Generated(
-            time = 1595457701315L,
-            codegenVersion = "1.0.15",
+            time = 1612206506050L,
+            codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
-            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\npublic  void setHostInputToken(android.os.IBinder)\nprivate  boolean extrasEquals(android.os.Bundle)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\npublic  void filterContentTypes()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic  void setHostInputToken(android.os.IBinder)\nprivate  boolean extrasEquals(android.os.Bundle)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\npublic  void filterContentTypes()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
index 0152387..c2ee646 100644
--- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
+++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
@@ -53,8 +53,10 @@
     public static final String KEY_CUR_TASK = "cur_task";
     /** Package of newly requested heavy-weight app. */
     public static final String KEY_NEW_APP = "new_app";
+    public static final String KEY_ACTIVITY_OPTIONS = "activity_options";
     
     IntentSender mStartIntent;
+    Bundle mActivityOptions;
     boolean mHasResult;
     String mCurApp;
     int mCurTask;
@@ -65,8 +67,9 @@
         super.onCreate(savedInstanceState);
         
         requestWindowFeature(Window.FEATURE_NO_TITLE);
-        
+
         mStartIntent = (IntentSender)getIntent().getParcelableExtra(KEY_INTENT);
+        mActivityOptions = getIntent().getBundleExtra(KEY_ACTIVITY_OPTIONS);
         mHasResult = getIntent().getBooleanExtra(KEY_HAS_RESULT, false);
         mCurApp = getIntent().getStringExtra(KEY_CUR_APP);
         mCurTask = getIntent().getIntExtra(KEY_CUR_TASK, 0);
@@ -148,9 +151,9 @@
                 if (mHasResult) {
                     startIntentSenderForResult(mStartIntent, -1, null,
                             Intent.FLAG_ACTIVITY_FORWARD_RESULT,
-                            Intent.FLAG_ACTIVITY_FORWARD_RESULT, 0);
+                            Intent.FLAG_ACTIVITY_FORWARD_RESULT, 0, mActivityOptions);
                 } else {
-                    startIntentSenderForResult(mStartIntent, -1, null, 0, 0, 0);
+                    startIntentSenderForResult(mStartIntent, -1, null, 0, 0, 0, mActivityOptions);
                 }
             } catch (IntentSender.SendIntentException ex) {
                 Log.w("HeavyWeightSwitcherActivity", "Failure starting", ex);
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index a5eb5f6..7aca36a 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -179,9 +179,10 @@
      *
      * @param changeId    the ID of the change that was overridden
      * @param packageName the app package name that was overridden
+     * @return {@code true} if an override existed
      * @throws SecurityException if overriding changes is not permitted
      */
-    void clearOverrideForTest(long changeId, String packageName);
+    boolean clearOverrideForTest(long changeId, String packageName);
 
     /**
      * Enables all compatibility changes that have enabledSinceTargetSdk ==
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index fc95275..af21f81 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -726,4 +726,43 @@
             }
         }
     }
+
+    /**
+     * A {@link ServiceConnector} that doesn't connect to anything.
+     *
+     * @param <T> the type of the {@link IInterface ipc interface} for the remote service
+     */
+    class NoOp<T extends IInterface> extends AndroidFuture<Object> implements ServiceConnector<T> {
+        {
+            completeExceptionally(new IllegalStateException("ServiceConnector is a no-op"));
+        }
+
+        @Override
+        public boolean run(@NonNull VoidJob<T> job) {
+            return false;
+        }
+
+        @Override
+        public AndroidFuture<Void> post(@NonNull VoidJob<T> job) {
+            return (AndroidFuture) this;
+        }
+
+        @Override
+        public <R> AndroidFuture<R> postForResult(@NonNull Job<T, R> job) {
+            return (AndroidFuture) this;
+        }
+
+        @Override
+        public <R> AndroidFuture<R> postAsync(@NonNull Job<T, CompletableFuture<R>> job) {
+            return (AndroidFuture) this;
+        }
+
+        @Override
+        public AndroidFuture<T> connect() {
+            return (AndroidFuture) this;
+        }
+
+        @Override
+        public void unbind() {}
+    }
 }
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 3d896c8..6e9bc84 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -18,10 +18,12 @@
 
 import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
 import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR;
@@ -114,6 +116,8 @@
     public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = 22;
     public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = 23;
     public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = 24;
+    public static final int CUJ_LAUNCHER_OPEN_ALL_APPS = 25;
+    public static final int CUJ_LAUNCHER_ALL_APPS_SCROLL = 26;
 
     private static final int NO_STATSD_LOGGING = -1;
 
@@ -147,6 +151,8 @@
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL,
     };
 
     private static volatile InteractionJankMonitor sInstance;
@@ -191,6 +197,8 @@
             CUJ_LOCKSCREEN_PIN_DISAPPEAR,
             CUJ_LOCKSCREEN_TRANSITION_FROM_AOD,
             CUJ_LOCKSCREEN_TRANSITION_TO_AOD,
+            CUJ_LAUNCHER_OPEN_ALL_APPS,
+            CUJ_LAUNCHER_ALL_APPS_SCROLL,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CujType {
@@ -457,6 +465,10 @@
                 return "CUJ_LOCKSCREEN_TRANSITION_FROM_AOD";
             case CUJ_LOCKSCREEN_TRANSITION_TO_AOD:
                 return "CUJ_LOCKSCREEN_TRANSITION_TO_AOD";
+            case CUJ_LAUNCHER_OPEN_ALL_APPS :
+                return "CUJ_LAUNCHER_OPEN_ALL_APPS";
+            case CUJ_LAUNCHER_ALL_APPS_SCROLL:
+                return "CUJ_LAUNCHER_ALL_APPS_SCROLL";
         }
         return "UNKNOWN";
     }
diff --git a/core/java/com/android/internal/os/CPU_OWNERS b/core/java/com/android/internal/os/CPU_OWNERS
new file mode 100644
index 0000000..5d3473c
--- /dev/null
+++ b/core/java/com/android/internal/os/CPU_OWNERS
@@ -0,0 +1,3 @@
+connoro@google.com
+dplotnikov@google.com
+rslawik@google.com  # Android Telemetry
diff --git a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
index fa552e3..50331e3 100644
--- a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
+++ b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
@@ -24,10 +24,7 @@
     }
 
     /** Returns whether total CPU time is measured. */
-    public static boolean isSupported() {
-        // TODO(b/174245730): Implement this check.
-        return true;
-    }
+    public static native boolean isSupported();
 
     /** Reads total CPU time from bpf map. */
     public static native boolean read(Callback callback);
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 1b07aa0..0aa54f5 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -1,5 +1,6 @@
 per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS
 per-file *Zygote* = file:/ZYGOTE_OWNERS
+per-file *Cpu* = file:CPU_OWNERS
 
 # BatteryStats
 per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index d196d4a..c8afea9 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -1091,4 +1091,11 @@
      * fully-feature Memory Tagging, rather than the static Tagged Pointers.
      */
     public static native boolean nativeSupportsTaggedPointers();
+
+    /**
+     * Returns the current native tagging level, as one of the
+     * MEMORY_TAG_LEVEL_* constants. Returns zero if no tagging is present, or
+     * we failed to determine the level.
+     */
+    public static native int nativeCurrentTaggingLevel();
 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 94e21e5..f2ed50e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -787,9 +787,19 @@
             Zygote.applyInvokeWithSystemProperty(parsedArgs);
 
             if (Zygote.nativeSupportsMemoryTagging()) {
-                /* The system server is more privileged than regular app processes, so it has async
-                 * tag checks enabled on hardware that supports memory tagging. */
-                parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
+                /* The system server has ASYNC MTE by default, in order to allow
+                 * system services to specify their own MTE level later, as you
+                 * can't re-enable MTE once it's disabled. */
+                String mode = SystemProperties.get("arm64.memtag.process.system_server", "async");
+                if (mode.equals("async")) {
+                    parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
+                } else if (mode.equals("sync")) {
+                    parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_SYNC;
+                } else if (!mode.equals("off")) {
+                    /* When we have an invalid memory tag level, keep the current level. */
+                    parsedArgs.mRuntimeFlags |= Zygote.nativeCurrentTaggingLevel();
+                    Slog.e(TAG, "Unknown memory tag level for the system server: \"" + mode + "\"");
+                }
             } else if (Zygote.nativeSupportsTaggedPointers()) {
                 /* Enable pointer tagging in the system server. Hardware support for this is present
                  * in all ARMv8 CPUs. */
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 2279d57..35d1d7b 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -5,6 +5,9 @@
 # Connectivity
 per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
 
+# CPU
+per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS
+
 # Display
 per-file android_hardware_display_* = file:/services/core/java/com/android/server/display/OWNERS
 
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 6337680..8977b97 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -40,7 +40,7 @@
 
     jmethodID dispatchVsync;
     jmethodID dispatchHotplug;
-    jmethodID dispatchConfigChanged;
+    jmethodID dispatchModeChanged;
     jmethodID dispatchFrameRateOverrides;
 
     struct {
@@ -69,8 +69,8 @@
     void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
                        VsyncEventData vsyncEventData) override;
     void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
-    void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
-                               int32_t configId, nsecs_t vsyncPeriod) override;
+    void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
+                             nsecs_t vsyncPeriod) override;
     void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
                                     std::vector<FrameRateOverride> overrides) override;
     void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) override {}
@@ -129,20 +129,19 @@
     mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
 }
 
-void NativeDisplayEventReceiver::dispatchConfigChanged(
-    nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId, nsecs_t) {
-  JNIEnv* env = AndroidRuntime::getJNIEnv();
+void NativeDisplayEventReceiver::dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+                                                     int32_t modeId, nsecs_t) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
 
-  ScopedLocalRef<jobject> receiverObj(env,
-                                      jniGetReferent(env, mReceiverWeakGlobal));
-  if (receiverObj.get()) {
-    ALOGV("receiver %p ~ Invoking config changed handler.", this);
-    env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchConfigChanged,
-                        timestamp, displayId.value, configId);
-    ALOGV("receiver %p ~ Returned from config changed handler.", this);
-  }
+    ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
+    if (receiverObj.get()) {
+        ALOGV("receiver %p ~ Invoking mode changed handler.", this);
+        env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchModeChanged,
+                            timestamp, displayId.value, modeId);
+        ALOGV("receiver %p ~ Returned from mode changed handler.", this);
+    }
 
-  mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
+    mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
 }
 
 void NativeDisplayEventReceiver::dispatchFrameRateOverrides(
@@ -173,7 +172,7 @@
         ALOGV("receiver %p ~ Returned from FrameRateOverride handler.", this);
     }
 
-    mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
+    mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
 }
 
 static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj,
@@ -243,8 +242,9 @@
                              "(JJIJJ)V");
     gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
             gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
-    gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env,
-           gDisplayEventReceiverClassInfo.clazz, "dispatchConfigChanged", "(JJI)V");
+    gDisplayEventReceiverClassInfo.dispatchModeChanged =
+            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchModeChanged",
+                             "(JJI)V");
     gDisplayEventReceiverClassInfo.dispatchFrameRateOverrides =
             GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz,
                              "dispatchFrameRateOverrides",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 8597308..7a3366a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -44,8 +44,8 @@
 #include <ui/BlurRegion.h>
 #include <ui/ConfigStoreTypes.h>
 #include <ui/DeviceProductInfo.h>
-#include <ui/DisplayConfig.h>
 #include <ui/DisplayInfo.h>
+#include <ui/DisplayMode.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicTypes.h>
@@ -98,8 +98,8 @@
     jfieldID refreshRate;
     jfieldID appVsyncOffsetNanos;
     jfieldID presentationDeadlineNanos;
-    jfieldID configGroup;
-} gDisplayConfigClassInfo;
+    jfieldID group;
+} gDisplayModeClassInfo;
 
 static struct {
     jfieldID bottom;
@@ -202,13 +202,13 @@
 static struct {
     jclass clazz;
     jmethodID ctor;
-    jfieldID defaultConfig;
+    jfieldID defaultMode;
     jfieldID allowGroupSwitching;
     jfieldID primaryRefreshRateMin;
     jfieldID primaryRefreshRateMax;
     jfieldID appRequestRefreshRateMin;
     jfieldID appRequestRefreshRateMax;
-} gDesiredDisplayConfigSpecsClassInfo;
+} gDesiredDisplayModeSpecsClassInfo;
 
 static struct {
     jclass clazz;
@@ -1028,101 +1028,97 @@
     return object;
 }
 
-static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz, jobject tokenObj) {
-    Vector<DisplayConfig> configs;
+static jobjectArray nativeGetDisplayModes(JNIEnv* env, jclass clazz, jobject tokenObj) {
+    Vector<ui::DisplayMode> modes;
     if (const auto token = ibinderForJavaObject(env, tokenObj); !token ||
-        SurfaceComposerClient::getDisplayConfigs(token, &configs) != NO_ERROR ||
-        configs.isEmpty()) {
+        SurfaceComposerClient::getDisplayModes(token, &modes) != NO_ERROR || modes.isEmpty()) {
         return nullptr;
     }
 
-    jobjectArray configArray =
-            env->NewObjectArray(configs.size(), gDisplayConfigClassInfo.clazz, nullptr);
+    jobjectArray modesArray =
+            env->NewObjectArray(modes.size(), gDisplayModeClassInfo.clazz, nullptr);
 
-    for (size_t c = 0; c < configs.size(); ++c) {
-        const DisplayConfig& config = configs[c];
-        jobject object =
-                env->NewObject(gDisplayConfigClassInfo.clazz, gDisplayConfigClassInfo.ctor);
-        env->SetIntField(object, gDisplayConfigClassInfo.width, config.resolution.getWidth());
-        env->SetIntField(object, gDisplayConfigClassInfo.height, config.resolution.getHeight());
-        env->SetFloatField(object, gDisplayConfigClassInfo.xDpi, config.xDpi);
-        env->SetFloatField(object, gDisplayConfigClassInfo.yDpi, config.yDpi);
+    for (size_t c = 0; c < modes.size(); ++c) {
+        const ui::DisplayMode& mode = modes[c];
+        jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor);
+        env->SetIntField(object, gDisplayModeClassInfo.width, mode.resolution.getWidth());
+        env->SetIntField(object, gDisplayModeClassInfo.height, mode.resolution.getHeight());
+        env->SetFloatField(object, gDisplayModeClassInfo.xDpi, mode.xDpi);
+        env->SetFloatField(object, gDisplayModeClassInfo.yDpi, mode.yDpi);
 
-        env->SetFloatField(object, gDisplayConfigClassInfo.refreshRate, config.refreshRate);
-        env->SetLongField(object, gDisplayConfigClassInfo.appVsyncOffsetNanos,
-                          config.appVsyncOffset);
-        env->SetLongField(object, gDisplayConfigClassInfo.presentationDeadlineNanos,
-                          config.presentationDeadline);
-        env->SetIntField(object, gDisplayConfigClassInfo.configGroup, config.configGroup);
-        env->SetObjectArrayElement(configArray, static_cast<jsize>(c), object);
+        env->SetFloatField(object, gDisplayModeClassInfo.refreshRate, mode.refreshRate);
+        env->SetLongField(object, gDisplayModeClassInfo.appVsyncOffsetNanos, mode.appVsyncOffset);
+        env->SetLongField(object, gDisplayModeClassInfo.presentationDeadlineNanos,
+                          mode.presentationDeadline);
+        env->SetIntField(object, gDisplayModeClassInfo.group, mode.group);
+        env->SetObjectArrayElement(modesArray, static_cast<jsize>(c), object);
         env->DeleteLocalRef(object);
     }
 
-    return configArray;
+    return modesArray;
 }
 
-static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
-                                                   jobject desiredDisplayConfigSpecs) {
+static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
+                                                 jobject DesiredDisplayModeSpecs) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == nullptr) return JNI_FALSE;
 
-    jint defaultConfig = env->GetIntField(desiredDisplayConfigSpecs,
-                                          gDesiredDisplayConfigSpecsClassInfo.defaultConfig);
+    size_t defaultMode =
+            static_cast<size_t>(env->GetIntField(DesiredDisplayModeSpecs,
+                                                 gDesiredDisplayModeSpecsClassInfo.defaultMode));
     jboolean allowGroupSwitching =
-            env->GetBooleanField(desiredDisplayConfigSpecs,
-                                 gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching);
+            env->GetBooleanField(DesiredDisplayModeSpecs,
+                                 gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching);
     jfloat primaryRefreshRateMin =
-            env->GetFloatField(desiredDisplayConfigSpecs,
-                               gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin);
+            env->GetFloatField(DesiredDisplayModeSpecs,
+                               gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMin);
     jfloat primaryRefreshRateMax =
-            env->GetFloatField(desiredDisplayConfigSpecs,
-                               gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax);
+            env->GetFloatField(DesiredDisplayModeSpecs,
+                               gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMax);
     jfloat appRequestRefreshRateMin =
-            env->GetFloatField(desiredDisplayConfigSpecs,
-                               gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMin);
+            env->GetFloatField(DesiredDisplayModeSpecs,
+                               gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMin);
     jfloat appRequestRefreshRateMax =
-            env->GetFloatField(desiredDisplayConfigSpecs,
-                               gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax);
+            env->GetFloatField(DesiredDisplayModeSpecs,
+                               gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMax);
 
-    size_t result = SurfaceComposerClient::setDesiredDisplayConfigSpecs(token, defaultConfig,
-                                                                        allowGroupSwitching,
-                                                                        primaryRefreshRateMin,
-                                                                        primaryRefreshRateMax,
-                                                                        appRequestRefreshRateMin,
-                                                                        appRequestRefreshRateMax);
+    size_t result = SurfaceComposerClient::setDesiredDisplayModeSpecs(token, defaultMode,
+                                                                      allowGroupSwitching,
+                                                                      primaryRefreshRateMin,
+                                                                      primaryRefreshRateMax,
+                                                                      appRequestRefreshRateMin,
+                                                                      appRequestRefreshRateMax);
     return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
 }
 
-static jobject nativeGetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj) {
+static jobject nativeGetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == nullptr) return nullptr;
 
-    int32_t defaultConfig;
+    size_t defaultMode;
     bool allowGroupSwitching;
     float primaryRefreshRateMin;
     float primaryRefreshRateMax;
     float appRequestRefreshRateMin;
     float appRequestRefreshRateMax;
-    if (SurfaceComposerClient::getDesiredDisplayConfigSpecs(token, &defaultConfig,
-                                                            &allowGroupSwitching,
-                                                            &primaryRefreshRateMin,
-                                                            &primaryRefreshRateMax,
-                                                            &appRequestRefreshRateMin,
-                                                            &appRequestRefreshRateMax) !=
-        NO_ERROR) {
+    if (SurfaceComposerClient::getDesiredDisplayModeSpecs(token, &defaultMode, &allowGroupSwitching,
+                                                          &primaryRefreshRateMin,
+                                                          &primaryRefreshRateMax,
+                                                          &appRequestRefreshRateMin,
+                                                          &appRequestRefreshRateMax) != NO_ERROR) {
         return nullptr;
     }
 
-    return env->NewObject(gDesiredDisplayConfigSpecsClassInfo.clazz,
-                          gDesiredDisplayConfigSpecsClassInfo.ctor, defaultConfig,
-                          allowGroupSwitching, primaryRefreshRateMin, primaryRefreshRateMax,
-                          appRequestRefreshRateMin, appRequestRefreshRateMax);
+    return env->NewObject(gDesiredDisplayModeSpecsClassInfo.clazz,
+                          gDesiredDisplayModeSpecsClassInfo.ctor, defaultMode, allowGroupSwitching,
+                          primaryRefreshRateMin, primaryRefreshRateMax, appRequestRefreshRateMin,
+                          appRequestRefreshRateMax);
 }
 
-static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) {
+static jint nativeGetActiveDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObj) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return -1;
-    return static_cast<jint>(SurfaceComposerClient::getActiveConfig(token));
+    return static_cast<jint>(SurfaceComposerClient::getActiveDisplayModeId(token));
 }
 
 static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) {
@@ -1784,16 +1780,16 @@
             (void*)nativeSetDisplaySize },
     {"nativeGetDisplayInfo", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayInfo;",
             (void*)nativeGetDisplayInfo },
-    {"nativeGetDisplayConfigs", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayConfig;",
-            (void*)nativeGetDisplayConfigs },
-    {"nativeGetActiveConfig", "(Landroid/os/IBinder;)I",
-            (void*)nativeGetActiveConfig },
-    {"nativeSetDesiredDisplayConfigSpecs",
-            "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;)Z",
-            (void*)nativeSetDesiredDisplayConfigSpecs },
-    {"nativeGetDesiredDisplayConfigSpecs",
-            "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;",
-            (void*)nativeGetDesiredDisplayConfigSpecs },
+    {"nativeGetDisplayModes", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayMode;",
+            (void*)nativeGetDisplayModes },
+    {"nativeGetActiveDisplayMode", "(Landroid/os/IBinder;)I",
+            (void*)nativeGetActiveDisplayMode },
+    {"nativeSetDesiredDisplayModeSpecs",
+            "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;)Z",
+            (void*)nativeSetDesiredDisplayModeSpecs },
+    {"nativeGetDesiredDisplayModeSpecs",
+            "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;",
+            (void*)nativeGetDesiredDisplayModeSpecs },
     {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I",
             (void*)nativeGetDisplayColorModes},
     {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;",
@@ -1902,19 +1898,19 @@
             GetFieldIDOrDie(env, infoClazz, "deviceProductInfo",
                             "Landroid/hardware/display/DeviceProductInfo;");
 
-    jclass configClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayConfig");
-    gDisplayConfigClassInfo.clazz = MakeGlobalRefOrDie(env, configClazz);
-    gDisplayConfigClassInfo.ctor = GetMethodIDOrDie(env, configClazz, "<init>", "()V");
-    gDisplayConfigClassInfo.width = GetFieldIDOrDie(env, configClazz, "width", "I");
-    gDisplayConfigClassInfo.height = GetFieldIDOrDie(env, configClazz, "height", "I");
-    gDisplayConfigClassInfo.xDpi = GetFieldIDOrDie(env, configClazz, "xDpi", "F");
-    gDisplayConfigClassInfo.yDpi = GetFieldIDOrDie(env, configClazz, "yDpi", "F");
-    gDisplayConfigClassInfo.refreshRate = GetFieldIDOrDie(env, configClazz, "refreshRate", "F");
-    gDisplayConfigClassInfo.appVsyncOffsetNanos =
-            GetFieldIDOrDie(env, configClazz, "appVsyncOffsetNanos", "J");
-    gDisplayConfigClassInfo.presentationDeadlineNanos =
-            GetFieldIDOrDie(env, configClazz, "presentationDeadlineNanos", "J");
-    gDisplayConfigClassInfo.configGroup = GetFieldIDOrDie(env, configClazz, "configGroup", "I");
+    jclass modeClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayMode");
+    gDisplayModeClassInfo.clazz = MakeGlobalRefOrDie(env, modeClazz);
+    gDisplayModeClassInfo.ctor = GetMethodIDOrDie(env, modeClazz, "<init>", "()V");
+    gDisplayModeClassInfo.width = GetFieldIDOrDie(env, modeClazz, "width", "I");
+    gDisplayModeClassInfo.height = GetFieldIDOrDie(env, modeClazz, "height", "I");
+    gDisplayModeClassInfo.xDpi = GetFieldIDOrDie(env, modeClazz, "xDpi", "F");
+    gDisplayModeClassInfo.yDpi = GetFieldIDOrDie(env, modeClazz, "yDpi", "F");
+    gDisplayModeClassInfo.refreshRate = GetFieldIDOrDie(env, modeClazz, "refreshRate", "F");
+    gDisplayModeClassInfo.appVsyncOffsetNanos =
+            GetFieldIDOrDie(env, modeClazz, "appVsyncOffsetNanos", "J");
+    gDisplayModeClassInfo.presentationDeadlineNanos =
+            GetFieldIDOrDie(env, modeClazz, "presentationDeadlineNanos", "J");
+    gDisplayModeClassInfo.group = GetFieldIDOrDie(env, modeClazz, "group", "I");
 
     jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect");
     gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I");
@@ -2005,24 +2001,23 @@
     gDisplayPrimariesClassInfo.white = GetFieldIDOrDie(env, displayPrimariesClazz, "white",
             "Landroid/view/SurfaceControl$CieXyz;");
 
-    jclass desiredDisplayConfigSpecsClazz =
-            FindClassOrDie(env, "android/view/SurfaceControl$DesiredDisplayConfigSpecs");
-    gDesiredDisplayConfigSpecsClassInfo.clazz =
-            MakeGlobalRefOrDie(env, desiredDisplayConfigSpecsClazz);
-    gDesiredDisplayConfigSpecsClassInfo.ctor =
-            GetMethodIDOrDie(env, gDesiredDisplayConfigSpecsClassInfo.clazz, "<init>", "(IZFFFF)V");
-    gDesiredDisplayConfigSpecsClassInfo.defaultConfig =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "defaultConfig", "I");
-    gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "allowGroupSwitching", "Z");
-    gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMin", "F");
-    gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMax", "F");
-    gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMin =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "appRequestRefreshRateMin", "F");
-    gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "appRequestRefreshRateMax", "F");
+    jclass DesiredDisplayModeSpecsClazz =
+            FindClassOrDie(env, "android/view/SurfaceControl$DesiredDisplayModeSpecs");
+    gDesiredDisplayModeSpecsClassInfo.clazz = MakeGlobalRefOrDie(env, DesiredDisplayModeSpecsClazz);
+    gDesiredDisplayModeSpecsClassInfo.ctor =
+            GetMethodIDOrDie(env, gDesiredDisplayModeSpecsClassInfo.clazz, "<init>", "(IZFFFF)V");
+    gDesiredDisplayModeSpecsClassInfo.defaultMode =
+            GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "defaultMode", "I");
+    gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching =
+            GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "allowGroupSwitching", "Z");
+    gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMin =
+            GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "primaryRefreshRateMin", "F");
+    gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMax =
+            GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "primaryRefreshRateMax", "F");
+    gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMin =
+            GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRefreshRateMin", "F");
+    gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMax =
+            GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRefreshRateMax", "F");
 
     jclass captureArgsClazz = FindClassOrDie(env, "android/view/SurfaceControl$CaptureArgs");
     gCaptureArgsClassInfo.pixelFormat = GetFieldIDOrDie(env, captureArgsClazz, "mPixelFormat", "I");
diff --git a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
index 7249238..d8446ca 100644
--- a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
@@ -20,6 +20,10 @@
 
 namespace android {
 
+static jboolean KernelCpuTotalBpfMapReader_isSupported(JNIEnv *, jobject) {
+    return android::bpf::isTrackingUidTimesSupported() ? JNI_TRUE : JNI_FALSE;
+}
+
 static jboolean KernelCpuTotalBpfMapReader_read(JNIEnv *env, jobject, jobject callback) {
     jclass callbackClass = env->GetObjectClass(callback);
     jmethodID callbackMethod = env->GetMethodID(callbackClass, "accept", "(IIJ)V");
@@ -47,6 +51,7 @@
 static const JNINativeMethod methods[] = {
         {"read", "(Lcom/android/internal/os/KernelCpuTotalBpfMapReader$Callback;)Z",
          (void *)KernelCpuTotalBpfMapReader_read},
+        {"isSupported", "()Z", (void *)KernelCpuTotalBpfMapReader_isSupported},
 };
 
 int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv *env) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3d1c38d..e801725 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -2506,6 +2506,36 @@
 #endif
 }
 
+static jint com_android_internal_os_Zygote_nativeCurrentTaggingLevel(JNIEnv* env, jclass) {
+#if defined(__aarch64__)
+  int level = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+  if (level < 0) {
+    ALOGE("Failed to get memory tag level: %s", strerror(errno));
+    return 0;
+  } else if (!(level & PR_TAGGED_ADDR_ENABLE)) {
+    return 0;
+  }
+  // TBI is only possible on non-MTE hardware.
+  if (!mte_supported()) {
+    return MEMORY_TAG_LEVEL_TBI;
+  }
+
+  switch (level & PR_MTE_TCF_MASK) {
+    case PR_MTE_TCF_NONE:
+      return 0;
+    case PR_MTE_TCF_SYNC:
+      return MEMORY_TAG_LEVEL_SYNC;
+    case PR_MTE_TCF_ASYNC:
+      return MEMORY_TAG_LEVEL_ASYNC;
+    default:
+      ALOGE("Unknown memory tagging level: %i", level);
+      return 0;
+  }
+#else // defined(__aarch64__)
+  return 0;
+#endif // defined(__aarch64__)
+}
+
 static const JNINativeMethod gMethods[] = {
         {"nativeForkAndSpecialize",
          "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
@@ -2545,6 +2575,8 @@
          (void*)com_android_internal_os_Zygote_nativeSupportsMemoryTagging},
         {"nativeSupportsTaggedPointers", "()Z",
          (void*)com_android_internal_os_Zygote_nativeSupportsTaggedPointers},
+        {"nativeCurrentTaggingLevel", "()I",
+         (void*)com_android_internal_os_Zygote_nativeCurrentTaggingLevel},
 };
 
 int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/res/res/layout/notification_template_part_chronometer.xml b/core/res/res/layout/notification_template_part_chronometer.xml
index c5ffbea..e4ebc76 100644
--- a/core/res/res/layout/notification_template_part_chronometer.xml
+++ b/core/res/res/layout/notification_template_part_chronometer.xml
@@ -15,7 +15,7 @@
 -->
 
 <Chronometer android:id="@+id/chronometer" xmlns:android="http://schemas.android.com/apk/res/android"
-    android:textAppearance="@style/TextAppearance.Material.Notification.Time"
+    android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_marginEnd="4dp"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 14df775..e567c3d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7969,9 +7969,17 @@
         <!-- A class name in the AppWidget's package to be launched to configure.
              If not supplied, then no activity will be launched. -->
         <attr name="configure" format="string" />
-        <!-- A preview of what the AppWidget will look like after it's configured.
-              If not supplied, the AppWidget's icon will be used. -->
+        <!-- A preview, in a drawable resource id, of what the AppWidget will look like after it's
+             configured.
+             If not supplied, the AppWidget's icon will be used. -->
         <attr name="previewImage" format="reference" />
+        <!-- The layout resource id of a preview of what the AppWidget will look like after it's
+             configured.
+             Unlike previewImage, previewLayout can better showcase AppWidget in different locales,
+             system themes, display sizes & density etc.
+             If supplied, this will take precedence over the previewImage on supported widget hosts.
+             Otherwise, previewImage will be used. -->
+        <attr name="previewLayout" format="reference" />
         <!-- The view id of the AppWidget subview which should be auto-advanced.
              by the widget's host. -->
         <attr name="autoAdvanceViewId" format="reference" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3790aa4..8c5f454 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3477,6 +3477,11 @@
     <!-- The alarm window (in milliseconds) that JobScheduler uses to enter the idle state -->
     <integer name="config_jobSchedulerIdleWindowSlop">300000</integer>
 
+    <!-- If true, jobs from background user will be restricted -->
+    <bool name="config_jobSchedulerRestrictBackgroundUser">false</bool>
+    <!-- The length of grace period after user becomes background user -->
+    <integer name="config_jobSchedulerUserGracePeriod">60000</integer>
+
     <!-- If true, all guest users created on the device will be ephemeral. -->
     <bool name="config_guestUserEphemeral">false</bool>
 
@@ -4707,7 +4712,4 @@
 
     <!-- Whether to select voice/data/sms preference without user confirmation -->
     <bool name="config_voice_data_sms_auto_fallback">false</bool>
-
-    <!-- Whether to enable the one-handed keyguard on the lock screen for wide-screen devices. -->
-    <bool name="config_enableOneHandedKeyguard">false</bool>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 30cfb89..97ec0f4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3061,6 +3061,7 @@
     <public name="nativeHeapZeroInit" />
     <!-- @hide @SystemApi -->
     <public name="hotwordDetectionService" />
+    <public name="previewLayout" />
   </public-group>
 
   <public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f6d1b7d..9b5f670 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4532,9 +4532,8 @@
      shown in the warning dialog about the accessibility shortcut. -->
     <string name="color_correction_feature_name">Color Correction</string>
 
-    <!-- TODO(b/170970602): remove translatable=false when RBC has official name and strings -->
-    <!-- Title of Reduce Bright Colors feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
-    <string name="reduce_bright_colors_feature_name" translatable="false">Reduce Bright Colors</string>
+    <!-- Title of Reduce Brightness feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
+    <string name="reduce_bright_colors_feature_name">Reduce Brightness</string>
 
     <!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility service. [CHAR LIMIT=none] -->
     <string name="accessibility_shortcut_enabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned on.</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ae5e2f5..c41b78e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2680,6 +2680,8 @@
 
   <java-symbol type="integer" name="config_jobSchedulerInactivityIdleThreshold" />
   <java-symbol type="integer" name="config_jobSchedulerIdleWindowSlop" />
+  <java-symbol type="bool" name="config_jobSchedulerRestrictBackgroundUser" />
+  <java-symbol type="integer" name="config_jobSchedulerUserGracePeriod" />
 
   <java-symbol type="style" name="Animation.ImmersiveModeConfirmation" />
 
@@ -4176,6 +4178,4 @@
   <java-symbol type="bool" name="config_telephony5gNonStandalone" />
 
   <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
-
-  <java-symbol type="bool" name="config_enableOneHandedKeyguard" />
 </resources>
diff --git a/core/tests/coretests/src/android/graphics/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java
new file mode 100644
index 0000000..eae41e3
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import static android.graphics.fonts.FontStyle.FONT_SLANT_ITALIC;
+import static android.graphics.fonts.FontStyle.FONT_SLANT_UPRIGHT;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
+import static android.text.FontConfig.FontFamily.VARIANT_COMPACT;
+import static android.text.FontConfig.FontFamily.VARIANT_DEFAULT;
+import static android.text.FontConfig.FontFamily.VARIANT_ELEGANT;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.graphics.fonts.FontStyle;
+import android.os.LocaleList;
+import android.text.FontConfig;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class FontListParserTest {
+
+    @Test
+    public void named() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family name='sans-serif'>"
+                + "  <font>test.ttf</font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("test.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", null)),
+                "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    @Test
+    public void fallback() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family lang='en'>"
+                + "  <font>test.ttf</font>"
+                + "  <font fallbackFor='serif'>test.ttf</font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("test.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", null),
+                        new FontConfig.Font(new File("test.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", "serif")),
+                null, LocaleList.forLanguageTags("en"), VARIANT_DEFAULT);
+
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    @Test
+    public void compact() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family lang='en' variant='compact'>"
+                + "  <font>test.ttf</font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("test.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", null)),
+                null, LocaleList.forLanguageTags("en"), VARIANT_COMPACT);
+
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    @Test
+    public void elegant() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family lang='en' variant='elegant'>"
+                + "  <font>test.ttf</font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("test.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", null)),
+                null, LocaleList.forLanguageTags("en"), VARIANT_ELEGANT);
+
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    @Test
+    public void styles() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family name='sans-serif'>"
+                + "  <font style='normal'>normal.ttf</font>"
+                + "  <font weight='100'>weight.ttf</font>"
+                + "  <font style='italic'>italic.ttf</font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("normal.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", null),
+                        new FontConfig.Font(new File("weight.ttf"), null,
+                                new FontStyle(100, FONT_SLANT_UPRIGHT),
+                                0, "", null),
+                        new FontConfig.Font(new File("italic.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_ITALIC),
+                                0, "", null)),
+                "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    @Test
+    public void variable() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family name='sans-serif'>"
+                + "  <font>test-VF.ttf"
+                + "    <axis tag='wdth' stylevalue='100' />"
+                + "    <axis tag='wght' stylevalue='200' />"
+                + "  </font>"
+                + "  <font>test-VF.ttf"
+                + "    <axis tag='wdth' stylevalue='400' />"
+                + "    <axis tag='wght' stylevalue='700' />"
+                + "  </font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("test-VF.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "'wdth' 100.0,'wght' 200.0", null),
+                        new FontConfig.Font(new File("test-VF.ttf"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "'wdth' 400.0,'wght' 700.0", null)),
+                "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    @Test
+    public void ttc() throws Exception {
+        String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<family name='sans-serif'>"
+                + "  <font index='0'>test.ttc</font>"
+                + "  <font index='1'>test.ttc</font>"
+                + "</family>";
+        FontConfig.FontFamily expected = new FontConfig.FontFamily(
+                Arrays.asList(
+                        new FontConfig.Font(new File("test.ttc"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                0, "", null),
+                        new FontConfig.Font(new File("test.ttc"), null,
+                                new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+                                1, "", null)),
+                "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+        FontConfig.FontFamily family = readFamily(xml);
+        assertThat(family).isEqualTo(expected);
+
+        String serialized = writeFamily(family);
+        assertWithMessage("serialized = " + serialized)
+                .that(readFamily(serialized)).isEqualTo(expected);
+    }
+
+    private FontConfig.FontFamily readFamily(String xml)
+            throws IOException, XmlPullParserException {
+        StandardCharsets.UTF_8.name();
+        ByteArrayInputStream buffer = new ByteArrayInputStream(
+                xml.getBytes(StandardCharsets.UTF_8));
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(buffer, "UTF-8");
+        parser.nextTag();
+        return FontListParser.readFamily(parser, "", null);
+    }
+
+    private String writeFamily(FontConfig.FontFamily family) throws IOException {
+        TypedXmlSerializer out = Xml.newFastSerializer();
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        out.setOutput(buffer, "UTF-8");
+        out.startTag(null, "family");
+        FontListParser.writeFamily(out, family);
+        out.endTag(null, "family");
+        out.endDocument();
+        return buffer.toString("UTF-8");
+    }
+}
diff --git a/core/tests/coretests/src/android/os/BrightnessLimit.java b/core/tests/coretests/src/android/os/BrightnessLimit.java
index be79355..219f741 100644
--- a/core/tests/coretests/src/android/os/BrightnessLimit.java
+++ b/core/tests/coretests/src/android/os/BrightnessLimit.java
@@ -42,7 +42,8 @@
 
     public void onClick(View v) {
         DisplayManager dm = getSystemService(DisplayManager.class);
-        dm.setTemporaryBrightness(0.0f);
+        final int displayId = getBaseContext().getDisplay().getDisplayId();
+        dm.setTemporaryBrightness(displayId, 0.0f);
         Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 0);
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/OWNERS b/core/tests/coretests/src/com/android/internal/os/OWNERS
index 4068e2b..3f8f9e2 100644
--- a/core/tests/coretests/src/com/android/internal/os/OWNERS
+++ b/core/tests/coretests/src/com/android/internal/os/OWNERS
@@ -1 +1,4 @@
 include /BATTERY_STATS_OWNERS
+
+# CPU
+per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index cdc61a1..0484a9a 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -465,10 +465,15 @@
         <permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
         <!-- Permission required for CTS test CarrierMessagingServiceWrapperTest -->
         <permission name="android.permission.BIND_CARRIER_SERVICES"/>
+        <!-- Permission required for CTS test - MusicRecognitionManagerTest -->
+        <permission name="android.permission.MANAGE_MUSIC_RECOGNITION" />
         <!-- Permission required for CTS test - CallLogTest -->
         <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
         <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
         <permission name="android.permission.MODIFY_QUIET_MODE" />
+        <!-- Permission required for GTS test - GtsAssistIntentTestCases -->
+        <permission name="android.permission.MANAGE_SOUND_TRIGGER" />
+        <permission name="android.permission.CAPTURE_AUDIO_HOTWORD" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
@@ -482,6 +487,8 @@
         <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
         <!-- Permissions required for quick settings tile -->
         <permission name="android.permission.STATUS_BAR"/>
+        <!-- Permissions required to query Betterbug -->
+        <permission name="android.permission.QUERY_ALL_PACKAGES"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.tv">
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index bb795cd..63df0db 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -25,6 +25,8 @@
 import android.os.Build;
 import android.os.LocaleList;
 import android.text.FontConfig;
+import android.text.TextUtils;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -45,6 +47,27 @@
  */
 public class FontListParser {
 
+    // XML constants for FontFamily.
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_LANG = "lang";
+    private static final String ATTR_VARIANT = "variant";
+    private static final String TAG_FONT = "font";
+    private static final String VARIANT_COMPACT = "compact";
+    private static final String VARIANT_ELEGANT = "elegant";
+
+    // XML constants for Font.
+    public static final String ATTR_INDEX = "index";
+    public static final String ATTR_WEIGHT = "weight";
+    public static final String ATTR_STYLE = "style";
+    public static final String ATTR_FALLBACK_FOR = "fallbackFor";
+    public static final String STYLE_ITALIC = "italic";
+    public static final String STYLE_NORMAL = "normal";
+    public static final String TAG_AXIS = "axis";
+
+    // XML constants for FontVariationAxis.
+    public static final String ATTR_TAG = "tag";
+    public static final String ATTR_STYLEVALUE = "stylevalue";
+
     /* Parse fallback list (no names) */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException {
@@ -148,7 +171,7 @@
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             final String tag = parser.getName();
-            if (tag.equals("font")) {
+            if (tag.equals(TAG_FONT)) {
                 fonts.add(readFont(parser, fontDir, updatableFontMap));
             } else {
                 skip(parser);
@@ -156,15 +179,41 @@
         }
         int intVariant = FontConfig.FontFamily.VARIANT_DEFAULT;
         if (variant != null) {
-            if (variant.equals("compact")) {
+            if (variant.equals(VARIANT_COMPACT)) {
                 intVariant = FontConfig.FontFamily.VARIANT_COMPACT;
-            } else if (variant.equals("elegant")) {
+            } else if (variant.equals(VARIANT_ELEGANT)) {
                 intVariant = FontConfig.FontFamily.VARIANT_ELEGANT;
             }
         }
         return new FontConfig.FontFamily(fonts, name, LocaleList.forLanguageTags(lang), intVariant);
     }
 
+    /**
+     * Write a family tag representing {@code fontFamily}. The tag should be started by the caller.
+     */
+    public static void writeFamily(TypedXmlSerializer out, FontConfig.FontFamily fontFamily)
+            throws IOException {
+        if (!TextUtils.isEmpty(fontFamily.getName())) {
+            out.attribute(null, ATTR_NAME, fontFamily.getName());
+        }
+        if (!fontFamily.getLocaleList().isEmpty()) {
+            out.attribute(null, ATTR_LANG, fontFamily.getLocaleList().toLanguageTags());
+        }
+        switch (fontFamily.getVariant()) {
+            case FontConfig.FontFamily.VARIANT_COMPACT:
+                out.attribute(null, ATTR_VARIANT, VARIANT_COMPACT);
+                break;
+            case FontConfig.FontFamily.VARIANT_ELEGANT:
+                out.attribute(null, ATTR_VARIANT, VARIANT_ELEGANT);
+                break;
+        }
+        for (FontConfig.Font font : fontFamily.getFontList()) {
+            out.startTag(null, TAG_FONT);
+            writeFont(out, font);
+            out.endTag(null, TAG_FONT);
+        }
+    }
+
     /** Matches leading and trailing XML whitespace. */
     private static final Pattern FILENAME_WHITESPACE_PATTERN =
             Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
@@ -175,13 +224,13 @@
             @Nullable Map<String, File> updatableFontMap)
             throws XmlPullParserException, IOException {
 
-        String indexStr = parser.getAttributeValue(null, "index");
+        String indexStr = parser.getAttributeValue(null, ATTR_INDEX);
         int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
         List<FontVariationAxis> axes = new ArrayList<>();
-        String weightStr = parser.getAttributeValue(null, "weight");
-        int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
-        boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
-        String fallbackFor = parser.getAttributeValue(null, "fallbackFor");
+        String weightStr = parser.getAttributeValue(null, ATTR_WEIGHT);
+        int weight = weightStr == null ? FontStyle.FONT_WEIGHT_NORMAL : Integer.parseInt(weightStr);
+        boolean isItalic = STYLE_ITALIC.equals(parser.getAttributeValue(null, ATTR_STYLE));
+        String fallbackFor = parser.getAttributeValue(null, ATTR_FALLBACK_FOR);
         StringBuilder filename = new StringBuilder();
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() == XmlPullParser.TEXT) {
@@ -189,7 +238,7 @@
             }
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             String tag = parser.getName();
-            if (tag.equals("axis")) {
+            if (tag.equals(TAG_AXIS)) {
                 axes.add(readAxis(parser));
             } else {
                 skip(parser);
@@ -237,14 +286,48 @@
         return null;
     }
 
+    private static void writeFont(TypedXmlSerializer out, FontConfig.Font font)
+            throws IOException {
+        if (font.getTtcIndex() != 0) {
+            out.attributeInt(null, ATTR_INDEX, font.getTtcIndex());
+        }
+        if (font.getStyle().getWeight() != FontStyle.FONT_WEIGHT_NORMAL) {
+            out.attributeInt(null, ATTR_WEIGHT, font.getStyle().getWeight());
+        }
+        if (font.getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC) {
+            out.attribute(null, ATTR_STYLE, STYLE_ITALIC);
+        } else {
+            out.attribute(null, ATTR_STYLE, STYLE_NORMAL);
+        }
+        if (!TextUtils.isEmpty(font.getFontFamilyName())) {
+            out.attribute(null, ATTR_FALLBACK_FOR, font.getFontFamilyName());
+        }
+        out.text(font.getFile().getName());
+        FontVariationAxis[] axes =
+                FontVariationAxis.fromFontVariationSettings(font.getFontVariationSettings());
+        if (axes != null) {
+            for (FontVariationAxis axis : axes) {
+                out.startTag(null, TAG_AXIS);
+                writeAxis(out, axis);
+                out.endTag(null, TAG_AXIS);
+            }
+        }
+    }
+
     private static FontVariationAxis readAxis(XmlPullParser parser)
             throws XmlPullParserException, IOException {
-        String tagStr = parser.getAttributeValue(null, "tag");
-        String styleValueStr = parser.getAttributeValue(null, "stylevalue");
+        String tagStr = parser.getAttributeValue(null, ATTR_TAG);
+        String styleValueStr = parser.getAttributeValue(null, ATTR_STYLEVALUE);
         skip(parser);  // axis tag is empty, ignore any contents and consume end tag
         return new FontVariationAxis(tagStr, Float.parseFloat(styleValueStr));
     }
 
+    private static void writeAxis(TypedXmlSerializer out, FontVariationAxis axis)
+            throws IOException {
+        out.attribute(null, ATTR_TAG, axis.getTag());
+        out.attributeFloat(null, ATTR_STYLEVALUE, axis.getStyleValue());
+    }
+
     /**
      * Reads alias elements
      */
@@ -255,7 +338,7 @@
         String weightStr = parser.getAttributeValue(null, "weight");
         int weight;
         if (weightStr == null) {
-            weight = 400;
+            weight = FontStyle.FONT_WEIGHT_NORMAL;
         } else {
             weight = Integer.parseInt(weightStr);
         }
diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java
index 496e470..ad4c3fe 100644
--- a/graphics/java/android/graphics/RenderEffect.java
+++ b/graphics/java/android/graphics/RenderEffect.java
@@ -190,7 +190,7 @@
     }
 
     /**
-     * Create a filter that applies the color filter to the provided RenderEffect
+     * Create a {@link RenderEffect} that applies the color filter to the provided RenderEffect
      *
      * @param colorFilter ColorFilter applied to the content in the input RenderEffect
      * @param renderEffect Source to be transformed by the specified {@link ColorFilter}
@@ -209,7 +209,7 @@
     }
 
     /**
-     * Create a filter that applies the color filter to the contents of the
+     * Create a {@link RenderEffect} that applies the color filter to the contents of the
      * {@link android.graphics.RenderNode} that this RenderEffect is installed on
      * @param colorFilter ColorFilter applied to the content in the input RenderEffect
      */
@@ -224,7 +224,7 @@
     }
 
     /**
-     * {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances
+     * Create a {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances
      * combined by the specified {@link BlendMode}
      *
      * @param dst The Dst pixels used in blending
@@ -248,8 +248,8 @@
     }
 
     /**
-     * Create a filter that composes 'inner' with 'outer', such that the results of 'inner' are
-     * treated as the source bitmap passed to 'outer', i.e.
+     * Create a {@link RenderEffect} that composes 'inner' with 'outer', such that the results of
+     * 'inner' are treated as the source bitmap passed to 'outer', i.e.
      *
      * <pre>
      * {@code
@@ -278,6 +278,18 @@
             );
     }
 
+    /**
+     * Create a {@link RenderEffect} that renders the contents of the input {@link Shader}.
+     * This is useful to create an input for other {@link RenderEffect} types such as
+     * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)}
+     * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} or
+     * {@link RenderEffect#createColorFilterEffect(ColorFilter, RenderEffect)}.
+     */
+    @NonNull
+    public static RenderEffect createShaderEffect(@NonNull Shader shader) {
+        return new RenderEffect(nativeCreateShaderEffect(shader.getNativeInstance()));
+    }
+
     private final long mNativeRenderEffect;
 
     /* only constructed from static factory methods */
@@ -305,5 +317,6 @@
     private static native long nativeCreateColorFilterEffect(long colorFilter, long nativeInput);
     private static native long nativeCreateBlendModeEffect(long dst, long src, int blendmode);
     private static native long nativeCreateChainEffect(long outer, long inner);
+    private static native long nativeCreateShaderEffect(long shader);
     private static native long nativeGetFinalizer();
 }
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 9214ff1..b153c99 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -26,8 +26,7 @@
 import android.graphics.RectF;
 import android.os.LocaleList;
 import android.os.ParcelFileDescriptor;
-import android.util.Log;
-import android.util.LongSparseArray;
+import android.text.TextUtils;
 import android.util.LongSparseLongArray;
 import android.util.TypedValue;
 
@@ -44,7 +43,6 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.ref.WeakReference;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.channels.FileChannel;
@@ -61,14 +59,9 @@
     private static final int STYLE_ITALIC = 1;
     private static final int STYLE_NORMAL = 0;
 
-    private static final Object MAP_LOCK = new Object();
-    // We need to have mapping from native ptr to Font object for later accessing from TextShape
-    // result since Typeface doesn't have reference to Font object and it is not always created from
-    // Font object. Sometimes Typeface is created in native layer only and there might not be Font
-    // object in Java layer. So, if not found in this cache, create new Font object for API user.
-    @GuardedBy("MAP_LOCK")
-    private static final LongSparseArray<WeakReference<Font>> FONT_PTR_MAP =
-            new LongSparseArray<>();
+    private static final NativeAllocationRegistry BUFFER_REGISTRY =
+            NativeAllocationRegistry.createMalloced(
+                    ByteBuffer.class.getClassLoader(), nGetReleaseNativeFont());
 
     private static final Object SOURCE_ID_LOCK = new Object();
     @GuardedBy("SOURCE_ID_LOCK")
@@ -79,9 +72,7 @@
      * A builder class for creating new Font.
      */
     public static final class Builder {
-        private static final NativeAllocationRegistry sFontRegistry =
-                NativeAllocationRegistry.createMalloced(Font.class.getClassLoader(),
-                    nGetReleaseNativeFont());
+
 
         private @Nullable ByteBuffer mBuffer;
         private @Nullable File mFile;
@@ -484,26 +475,15 @@
             final String filePath = mFile == null ? "" : mFile.getAbsolutePath();
 
             long ptr;
-            int fontIdentifier;
+            final Font font;
             if (mFont == null) {
                 ptr = nBuild(builderPtr, readonlyBuffer, filePath, mLocaleList, mWeight, italic,
                         mTtcIndex);
-                long fontBufferPtr = nGetFontBufferAddress(ptr);
-                synchronized (SOURCE_ID_LOCK) {
-                    long id = FONT_SOURCE_ID_MAP.get(fontBufferPtr, -1);
-                    if (id == -1) {
-                        id = FONT_SOURCE_ID_MAP.size();
-                        FONT_SOURCE_ID_MAP.put(fontBufferPtr, id);
-                    }
-                    fontIdentifier = (int) id;
-                }
+                font = new Font(ptr);
             } else {
                 ptr = nClone(mFont.getNativePtr(), builderPtr, mWeight, italic, mTtcIndex);
-                fontIdentifier = mFont.mSourceIdentifier;
+                font = new Font(ptr);
             }
-            final Font font = new Font(ptr, readonlyBuffer, mFile,
-                    new FontStyle(mWeight, slant), mTtcIndex, mAxes, mLocaleList, fontIdentifier);
-            sFontRegistry.registerNativeAllocation(font, ptr);
             return font;
         }
 
@@ -525,33 +505,32 @@
     }
 
     private final long mNativePtr;  // address of the shared ptr of minikin::Font
-    private final @NonNull ByteBuffer mBuffer;
-    private final @Nullable File mFile;
-    private final FontStyle mFontStyle;
-    private final @IntRange(from = 0) int mTtcIndex;
-    private final @Nullable FontVariationAxis[] mAxes;
-    private final @NonNull String mLocaleList;
-    private final int mSourceIdentifier;  // An identifier of font source data.
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private @NonNull ByteBuffer mBuffer = null;
+    @GuardedBy("mLock")
+    private boolean mIsFileInitialized = false;
+    @GuardedBy("mLock")
+    private @Nullable File mFile = null;
+    @GuardedBy("mLock")
+    private FontStyle mFontStyle = null;
+    @GuardedBy("mLock")
+    private @Nullable FontVariationAxis[] mAxes = null;
+    @GuardedBy("mLock")
+    private @NonNull LocaleList mLocaleList = null;
+    @GuardedBy("mLock")
+    private int mSourceIdentifier = -1;
 
     /**
      * Use Builder instead
+     *
+     * Caller must increment underlying minikin::Font ref count.
+     *
+     * @hide
      */
-    private Font(long nativePtr, @NonNull ByteBuffer buffer, @Nullable File file,
-            @NonNull FontStyle fontStyle, @IntRange(from = 0) int ttcIndex,
-            @Nullable FontVariationAxis[] axes, @NonNull String localeList,
-            int sourceIdentifier) {
-        mBuffer = buffer;
-        mFile = file;
-        mFontStyle = fontStyle;
+    public Font(long nativePtr) {
         mNativePtr = nativePtr;
-        mTtcIndex = ttcIndex;
-        mAxes = axes;
-        mLocaleList = localeList;
-        mSourceIdentifier = sourceIdentifier;
-
-        synchronized (MAP_LOCK) {
-            FONT_PTR_MAP.append(nGetNativeFontPtr(mNativePtr), new WeakReference<>(this));
-        }
     }
 
     /**
@@ -563,7 +542,22 @@
      * @return a font buffer
      */
     public @NonNull ByteBuffer getBuffer() {
-        return mBuffer;
+        synchronized (mLock) {
+            if (mBuffer == null) {
+                // Create new instance of native FontWrapper, i.e. incrementing ref count of
+                // minikin Font instance for keeping buffer fo ByteBuffer reference which may live
+                // longer than this object.
+                long ref = nCloneFont(mNativePtr);
+                ByteBuffer fromNative = nNewByteBuffer(mNativePtr);
+
+                // Bind ByteBuffer's lifecycle with underlying font object.
+                BUFFER_REGISTRY.registerNativeAllocation(fromNative, ref);
+
+                // JNI NewDirectBuffer creates writable ByteBuffer even if it is mmaped readonly.
+                mBuffer = fromNative.asReadOnlyBuffer();
+            }
+            return mBuffer;
+        }
     }
 
     /**
@@ -574,7 +568,16 @@
      * @return a file path of the font
      */
     public @Nullable File getFile() {
-        return mFile;
+        synchronized (mLock) {
+            if (!mIsFileInitialized) {
+                String path = nGetFontPath(mNativePtr);
+                if (!TextUtils.isEmpty(path)) {
+                    mFile = new File(path);
+                }
+                mIsFileInitialized = true;
+            }
+            return mFile;
+        }
     }
 
     /**
@@ -585,7 +588,16 @@
      * @return a font style
      */
     public @NonNull FontStyle getStyle() {
-        return mFontStyle;
+        synchronized (mLock) {
+            if (mFontStyle == null) {
+                int packedStyle = nGetPackedStyle(mNativePtr);
+                mFontStyle = new FontStyle(
+                        FontFileUtil.unpackWeight(packedStyle),
+                        FontFileUtil.unpackItalic(packedStyle)
+                                ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT);
+            }
+            return mFontStyle;
+        }
     }
 
     /**
@@ -597,7 +609,7 @@
      * @return a TTC index value
      */
     public @IntRange(from = 0) int getTtcIndex() {
-        return mTtcIndex;
+        return nGetIndex(mNativePtr);
     }
 
     /**
@@ -608,7 +620,23 @@
      * @return font variation settings
      */
     public @Nullable FontVariationAxis[] getAxes() {
-        return mAxes == null ? null : mAxes.clone();
+        synchronized (mLock) {
+            if (mAxes == null) {
+                int axisCount = nGetAxisCount(mNativePtr);
+                mAxes = new FontVariationAxis[axisCount];
+                char[] charBuffer = new char[4];
+                for (int i = 0; i < axisCount; ++i) {
+                    long packedAxis = nGetAxisInfo(mNativePtr, i);
+                    float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
+                    charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >>> 56);
+                    charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >>> 48);
+                    charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >>> 40);
+                    charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >>> 32);
+                    mAxes[i] = new FontVariationAxis(new String(charBuffer), value);
+                }
+            }
+        }
+        return mAxes;
     }
 
     /**
@@ -618,7 +646,17 @@
      * @return a locale list
      */
     public @NonNull LocaleList getLocaleList() {
-        return LocaleList.forLanguageTags(mLocaleList);
+        synchronized (mLock) {
+            if (mLocaleList == null) {
+                String langTags = nGetLocaleList(mNativePtr);
+                if (TextUtils.isEmpty(langTags)) {
+                    mLocaleList = LocaleList.getEmptyLocaleList();
+                } else {
+                    mLocaleList = LocaleList.forLanguageTags(langTags);
+                }
+            }
+            return mLocaleList;
+        }
     }
 
     /**
@@ -713,7 +751,20 @@
      * @return an unique identifier for the font source data.
      */
     public int getSourceIdentifier() {
-        return mSourceIdentifier;
+        synchronized (mLock) {
+            if (mSourceIdentifier == -1) {
+                long bufferAddress = nGetBufferAddress(mNativePtr);
+                synchronized (SOURCE_ID_LOCK) {
+                    long id = FONT_SOURCE_ID_MAP.get(bufferAddress, -1);
+                    if (id == -1) {
+                        id = FONT_SOURCE_ID_MAP.size();
+                        FONT_SOURCE_ID_MAP.append(bufferAddress, id);
+                    }
+                    mSourceIdentifier = (int) id;
+                }
+            }
+            return mSourceIdentifier;
+        }
     }
 
     /**
@@ -736,13 +787,16 @@
     private boolean isSameSource(@NonNull Font other) {
         Objects.requireNonNull(other);
 
+        ByteBuffer myBuffer = getBuffer();
+        ByteBuffer otherBuffer = other.getBuffer();
+
         // Shortcut for the same instance.
-        if (mBuffer == other.mBuffer) {
+        if (myBuffer == otherBuffer) {
             return true;
         }
 
         // Shortcut for different font buffer check by comparing size.
-        if (mBuffer.capacity() != other.mBuffer.capacity()) {
+        if (myBuffer.capacity() != otherBuffer.capacity()) {
             return false;
         }
 
@@ -750,15 +804,15 @@
         // underlying native font object holds buffer address, check if this buffer points exactly
         // the same address as a shortcut of equality. For being compatible with of API30 or before,
         // check buffer position even if the buffer points the same address.
-        if (mSourceIdentifier == other.mSourceIdentifier
-                && mBuffer.position() == other.mBuffer.position()) {
+        if (getSourceIdentifier() == other.getSourceIdentifier()
+                && myBuffer.position() == otherBuffer.position()) {
             return true;
         }
 
         // Unfortunately, need to compare bytes one-by-one since the buffer may be different font
         // file but has the same file size, or two font has same content but they are allocated
         // differently. For being compatible with API30 ore before, compare with ByteBuffer#equals.
-        return mBuffer.equals(other.mBuffer);
+        return myBuffer.equals(otherBuffer);
     }
 
     @Override
@@ -769,10 +823,20 @@
         if (!(o instanceof Font)) {
             return false;
         }
+
         Font f = (Font) o;
-        boolean paramEqual = mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex
-                && Arrays.equals(f.mAxes, mAxes) && Objects.equals(f.mLocaleList, mLocaleList)
-                && Objects.equals(mFile, f.mFile);
+
+        // The underlying minikin::Font object is the source of the truth of font information. Thus,
+        // Pointer equality is the object equality.
+        if (nGetMinikinFontPtr(mNativePtr) == nGetMinikinFontPtr(f.mNativePtr)) {
+            return true;
+        }
+
+        boolean paramEqual = f.getStyle().equals(getStyle())
+                && f.getTtcIndex() == getTtcIndex()
+                && Arrays.equals(f.getAxes(), getAxes())
+                && Objects.equals(f.getLocaleList(), getLocaleList())
+                && Objects.equals(getFile(), f.getFile());
 
         if (!paramEqual) {
             return false;
@@ -784,64 +848,42 @@
     @Override
     public int hashCode() {
         return Objects.hash(
-                mFontStyle,
-                mTtcIndex,
-                Arrays.hashCode(mAxes),
+                getStyle(),
+                getTtcIndex(),
+                Arrays.hashCode(getAxes()),
                 // Use Buffer size instead of ByteBuffer#hashCode since ByteBuffer#hashCode traverse
                 // data which is not performant e.g. for HashMap. The hash collision are less likely
                 // happens because it is unlikely happens the different font files has exactly the
                 // same size.
-                mLocaleList);
+                getLocaleList());
     }
 
     @Override
     public String toString() {
         return "Font {"
-            + "path=" + mFile
-            + ", style=" + mFontStyle
-            + ", ttcIndex=" + mTtcIndex
-            + ", axes=" + FontVariationAxis.toFontVariationSettings(mAxes)
-            + ", localeList=" + mLocaleList
-            + ", buffer=" + mBuffer
+            + "path=" + getFile()
+            + ", style=" + getStyle()
+            + ", ttcIndex=" + getTtcIndex()
+            + ", axes=" + FontVariationAxis.toFontVariationSettings(getAxes())
+            + ", localeList=" + getLocaleList()
+            + ", buffer=" + getBuffer()
             + "}";
     }
 
-    /**
-     * Lookup Font object from native pointer or create new one if not found.
-     * @hide
-     */
-    public static Font findOrCreateFontFromNativePtr(long ptr) {
-        // First, lookup from known mapps.
-        synchronized (MAP_LOCK) {
-            WeakReference<Font> fontRef = FONT_PTR_MAP.get(ptr);
-            if (fontRef != null) {
-                Font font = fontRef.get();
-                if (font != null) {
-                    return font;
-                }
-            }
+    @CriticalNative
+    private static native long nGetMinikinFontPtr(long font);
 
-            // If not found, create Font object from native object for Java API users.
-            ByteBuffer buffer = NativeFontBufferHelper.refByteBuffer(ptr);
-            NativeFont.Font font = NativeFont.readNativeFont(ptr);
+    @CriticalNative
+    private static native long nCloneFont(long font);
 
-            Font.Builder builder = new Font.Builder(buffer, font.getFile(), "")
-                    .setWeight(font.getStyle().getWeight())
-                    .setSlant(font.getStyle().getSlant())
-                    .setTtcIndex(font.getIndex())
-                    .setFontVariationSettings(font.getAxes());
+    @FastNative
+    private static native ByteBuffer nNewByteBuffer(long font);
 
-            Font newFont = null;
-            try {
-                newFont = builder.build();
-                FONT_PTR_MAP.append(ptr, new WeakReference<>(newFont));
-            } catch (IOException e) {
-                // This must not happen since the buffer was already created once.
-                Log.e("Font", "Failed to create font object from existing buffer.", e);
-            }
-            return newFont;
-        }
-    }
+    @CriticalNative
+    private static native long nGetBufferAddress(long font);
+
+    @CriticalNative
+    private static native long nGetReleaseNativeFont();
 
     @FastNative
     private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);
@@ -849,9 +891,21 @@
     @FastNative
     private static native float nGetFontMetrics(long font, long paint, Paint.FontMetrics metrics);
 
-    @CriticalNative
-    private static native long nGetNativeFontPtr(long ptr);
+    @FastNative
+    private static native String nGetFontPath(long fontPtr);
+
+    @FastNative
+    private static native String nGetLocaleList(long familyPtr);
 
     @CriticalNative
-    private static native long nGetFontBufferAddress(long font);
+    private static native int nGetPackedStyle(long fontPtr);
+
+    @CriticalNative
+    private static native int nGetIndex(long fontPtr);
+
+    @CriticalNative
+    private static native int nGetAxisCount(long fontPtr);
+
+    @CriticalNative
+    private static native long nGetAxisInfo(long fontPtr, int i);
 }
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index 77f86fe..8c13d3e 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -143,12 +143,10 @@
         private static native long nGetReleaseNativeFamily();
     }
 
-    private final ArrayList<Font> mFonts;
     private final long mNativePtr;
 
     // Use Builder instead.
     private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
-        mFonts = fonts;
         mNativePtr = ptr;
     }
 
@@ -176,7 +174,10 @@
      * @return a registered font
      */
     public @NonNull Font getFont(@IntRange(from = 0) int index) {
-        return mFonts.get(index);
+        if (index < 0 || getSize() <= index) {
+            throw new IndexOutOfBoundsException();
+        }
+        return new Font(nGetFont(mNativePtr, index));
     }
 
     /**
@@ -185,7 +186,7 @@
      * @return the number of fonts registered in this family.
      */
     public @IntRange(from = 1) int getSize() {
-        return mFonts.size();
+        return nGetFontSize(mNativePtr);
     }
 
     /** @hide */
@@ -193,6 +194,12 @@
         return mNativePtr;
     }
 
+    @CriticalNative
+    private static native int nGetFontSize(long family);
+
+    @CriticalNative
+    private static native long nGetFont(long family, int i);
+
     @FastNative
     private static native String nGetLangTags(long family);
 
diff --git a/graphics/java/android/graphics/fonts/NativeFont.java b/graphics/java/android/graphics/fonts/NativeFont.java
deleted file mode 100644
index 9e9d76a..0000000
--- a/graphics/java/android/graphics/fonts/NativeFont.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.fonts;
-
-import android.graphics.Typeface;
-
-import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Read native font objects.
- *
- * @hide
- */
-public class NativeFont {
-
-    /**
-     * Represents native font object.
-     */
-    public static final class Font {
-        private final File mFile;
-        private final int mIndex;
-        private final FontVariationAxis[] mAxes;
-        private final FontStyle mStyle;
-
-        public Font(File file, int index, FontVariationAxis[] axes, FontStyle style) {
-            mFile = file;
-            mIndex = index;
-            mAxes = axes;
-            mStyle = style;
-        }
-
-        public File getFile() {
-            return mFile;
-        }
-
-        public FontVariationAxis[] getAxes() {
-            return mAxes;
-        }
-
-        public FontStyle getStyle() {
-            return mStyle;
-        }
-
-        public int getIndex() {
-            return mIndex;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-            Font font = (Font) o;
-            return mIndex == font.mIndex && mFile.equals(font.mFile)
-                    && Arrays.equals(mAxes, font.mAxes) && mStyle.equals(font.mStyle);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = Objects.hash(mFile, mIndex, mStyle);
-            result = 31 * result + Arrays.hashCode(mAxes);
-            return result;
-        }
-    }
-
-    /**
-     * Represents native font family object.
-     */
-    public static final class Family {
-        private final List<Font> mFonts;
-        private final String mLocale;
-
-        public Family(List<Font> fonts, String locale) {
-            mFonts = fonts;
-            mLocale = locale;
-        }
-
-        public List<Font> getFonts() {
-            return mFonts;
-        }
-
-        public String getLocale() {
-            return mLocale;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-            Family family = (Family) o;
-            return mFonts.equals(family.mFonts) && mLocale.equals(family.mLocale);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mFonts, mLocale);
-        }
-    }
-
-    /**
-     * Get underlying font families from Typeface
-     *
-     * @param typeface a typeface
-     * @return list of family
-     */
-    public static List<Family> readTypeface(Typeface typeface) {
-        int familyCount = nGetFamilyCount(typeface.native_instance);
-        List<Family> result = new ArrayList<>(familyCount);
-        for (int i = 0; i < familyCount; ++i) {
-            result.add(readNativeFamily(nGetFamily(typeface.native_instance, i)));
-        }
-        return result;
-    }
-
-    /**
-     * Read family object from native pointer
-     *
-     * @param familyPtr a font family pointer
-     * @return a family
-     */
-    public static Family readNativeFamily(long familyPtr) {
-        int fontCount = nGetFontCount(familyPtr);
-        List<Font> result = new ArrayList<>(fontCount);
-        for (int i = 0; i < fontCount; ++i) {
-            result.add(readNativeFont(nGetFont(familyPtr, i)));
-        }
-        String localeList = nGetLocaleList(familyPtr);
-        return new Family(result, localeList);
-    }
-
-    /**
-     * Read font object from native pointer.
-     *
-     * @param ptr a font pointer
-     * @return a font
-     */
-    public static Font readNativeFont(long ptr) {
-        long packed = nGetFontInfo(ptr);
-        int weight = (int) (packed & 0x0000_0000_0000_FFFFL);
-        boolean italic = (packed & 0x0000_0000_0001_0000L) != 0;
-        int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32);
-        int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48);
-        FontVariationAxis[] axes = new FontVariationAxis[axisCount];
-        char[] charBuffer = new char[4];
-        for (int i = 0; i < axisCount; ++i) {
-            long packedAxis = nGetAxisInfo(ptr, i);
-            float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
-            charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56);
-            charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48);
-            charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40);
-            charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32);
-            axes[i] = new FontVariationAxis(new String(charBuffer), value);
-        }
-        String path = nGetFontPath(ptr);
-        File file = (path == null) ? null : new File(path);
-        FontStyle style = new FontStyle(weight,
-                italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT);
-
-        return new Font(file, ttcIndex, axes, style);
-    }
-
-    @CriticalNative
-    private static native int nGetFamilyCount(long ptr);
-
-    @CriticalNative
-    private static native long nGetFamily(long ptr, int index);
-
-    @FastNative
-    private static native String nGetLocaleList(long familyPtr);
-
-    @CriticalNative
-    private static native long nGetFont(long familyPtr, int fontIndex);
-
-    @CriticalNative
-    private static native int nGetFontCount(long familyPtr);
-
-    @CriticalNative
-    private static native long nGetFontInfo(long fontPtr);
-
-    @CriticalNative
-    private static native long nGetAxisInfo(long fontPtr, int i);
-
-    @FastNative
-    private static native String nGetFontPath(long fontPtr);
-}
diff --git a/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java b/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java
deleted file mode 100644
index 5655e7f..0000000
--- a/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.fonts;
-
-import android.annotation.NonNull;
-
-import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
-
-import libcore.util.NativeAllocationRegistry;
-
-import java.nio.ByteBuffer;
-
-/**
- * This is a helper class for showing native allocated buffer in Java API.
- *
- * @hide
- */
-public class NativeFontBufferHelper {
-    private NativeFontBufferHelper() {}
-
-    private static final NativeAllocationRegistry REGISTRY =
-            NativeAllocationRegistry.createMalloced(
-                    ByteBuffer.class.getClassLoader(), nGetReleaseFunc());
-
-    /**
-     * Wrap native buffer with ByteBuffer with adding reference to it.
-     */
-    public static @NonNull ByteBuffer refByteBuffer(long fontPtr) {
-        long refPtr = nRefFontBuffer(fontPtr);
-        ByteBuffer buffer = nWrapByteBuffer(refPtr);
-
-        // Releasing native object so that decreasing shared pointer ref count when the byte buffer
-        // is GCed.
-        REGISTRY.registerNativeAllocation(buffer, refPtr);
-
-        return buffer;
-    }
-
-    @CriticalNative
-    private static native long nRefFontBuffer(long fontPtr);
-
-    @FastNative
-    private static native ByteBuffer nWrapByteBuffer(long refPtr);
-
-    @CriticalNative
-    private static native long nGetReleaseFunc();
-}
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index c2de0ac..8d20e9c 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -184,7 +184,7 @@
             long ptr = nGetFont(layoutPtr, i);
             if (prevPtr != ptr) {
                 prevPtr = ptr;
-                prevFont = Font.findOrCreateFontFromNativePtr(ptr);
+                prevFont = new Font(ptr);
             }
             mFonts.add(prevFont);
         }
@@ -224,9 +224,7 @@
             if (getGlyphId(i) != that.getGlyphId(i)) return false;
             if (getGlyphX(i) != that.getGlyphX(i)) return false;
             if (getGlyphY(i) != that.getGlyphY(i)) return false;
-            // Intentionally using reference equality since font equality is heavy due to buffer
-            // compare.
-            if (getFont(i) != that.getFont(i)) return false;
+            if (!getFont(i).equals(that.getFont(i))) return false;
         }
 
         return true;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 6a92980..70e30d2 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -585,6 +585,30 @@
                     mSpec.getKeyValidityForConsumptionEnd()
             ));
         }
+        if (mSpec.getCertificateNotAfter() != null) {
+            params.add(KeyStore2ParameterUtils.makeDate(
+                    KeymasterDefs.KM_TAG_CERTIFICATE_NOT_AFTER,
+                    mSpec.getCertificateNotAfter()
+            ));
+        }
+        if (mSpec.getCertificateNotBefore() != null) {
+            params.add(KeyStore2ParameterUtils.makeDate(
+                    KeymasterDefs.KM_TAG_CERTIFICATE_NOT_BEFORE,
+                    mSpec.getCertificateNotBefore()
+            ));
+        }
+        if (mSpec.getCertificateSerialNumber() != null) {
+            params.add(KeyStore2ParameterUtils.makeBignum(
+                    KeymasterDefs.KM_TAG_CERTIFICATE_SERIAL,
+                    mSpec.getCertificateSerialNumber()
+            ));
+        }
+        if (mSpec.getCertificateSubject() != null) {
+            params.add(KeyStore2ParameterUtils.makeBytes(
+                    KeymasterDefs.KM_TAG_CERTIFICATE_SUBJECT,
+                    mSpec.getCertificateSubject().getEncoded()
+            ));
+        }
 
         if (mSpec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
             params.add(KeyStore2ParameterUtils.makeInt(
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index 4c8ab8d..dcdd7de 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -28,6 +28,7 @@
 import android.security.keystore.UserAuthArgs;
 import android.system.keystore2.Authorization;
 
+import java.math.BigInteger;
 import java.security.ProviderException;
 import java.util.ArrayList;
 import java.util.Date;
@@ -154,6 +155,23 @@
     }
 
     /**
+     * This function constructs a {@link KeyParameter} expressing a Bignum.
+     * @param tag Must be KeyMint tag with the associated type BIGNUM.
+     * @param b A BitInteger to be stored in the new key parameter.
+     * @return An instance of {@link KeyParameter}.
+     * @hide
+     */
+    static @NonNull KeyParameter makeBignum(int tag, @NonNull BigInteger b) {
+        if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BIGNUM) {
+            throw new IllegalArgumentException("Not a bignum tag: " + tag);
+        }
+        KeyParameter p = new KeyParameter();
+        p.tag = tag;
+        p.value = KeyParameterValue.blob(b.toByteArray());
+        return p;
+    }
+
+    /**
      * This function constructs a {@link KeyParameter} expressing date.
      * @param tag Must be KeyMint tag with the associated type DATE.
      * @param date A date
@@ -167,10 +185,6 @@
         KeyParameter p = new KeyParameter();
         p.tag = tag;
         p.value = KeyParameterValue.dateTime(date.getTime());
-        if (p.value.getDateTime() < 0) {
-            throw new IllegalArgumentException("Date tag value out of range: "
-                    + p.value.getDateTime());
-        }
         return p;
     }
     /**
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
index 93a6e7b..b581f55 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml
@@ -97,14 +97,4 @@
         android:padding="@dimen/pip_resize_handle_padding"
         android:src="@drawable/pip_resize_handle"
         android:background="?android:selectableItemBackgroundBorderless" />
-
-    <!-- invisible layer to trap the focus, b/169372603 -->
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@null"
-        android:defaultFocusHighlightEnabled="false"
-        android:focusable="true"
-        android:focusableInTouchMode="true"
-        android:focusedByDefault="true" />
-t </FrameLayout>
+</FrameLayout>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index fe97e24..982cc00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -25,6 +25,7 @@
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.io.PrintWriter;
 import java.util.Optional;
@@ -38,7 +39,7 @@
     private static final String TAG = ShellCommandHandlerImpl.class.getSimpleName();
 
     private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
-    private final Optional<SplitScreen> mSplitScreenOptional;
+    private final Optional<SplitScreenController> mSplitScreenOptional;
     private final Optional<Pip> mPipOptional;
     private final Optional<OneHanded> mOneHandedOptional;
     private final Optional<HideDisplayCutout> mHideDisplayCutout;
@@ -50,7 +51,7 @@
     public static ShellCommandHandler create(
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<LegacySplitScreen> legacySplitScreenOptional,
-            Optional<SplitScreen> splitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             Optional<Pip> pipOptional,
             Optional<OneHanded> oneHandedOptional,
             Optional<HideDisplayCutout> hideDisplayCutout,
@@ -64,7 +65,7 @@
     private ShellCommandHandlerImpl(
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<LegacySplitScreen> legacySplitScreenOptional,
-            Optional<SplitScreen> splitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             Optional<Pip> pipOptional,
             Optional<OneHanded> oneHandedOptional,
             Optional<HideDisplayCutout> hideDisplayCutout,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index 0958a07..925bf4b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -25,6 +25,7 @@
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.Optional;
@@ -39,7 +40,7 @@
     private final DragAndDropController mDragAndDropController;
     private final ShellTaskOrganizer mShellTaskOrganizer;
     private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
-    private final Optional<SplitScreen> mSplitScreenOptional;
+    private final Optional<SplitScreenController> mSplitScreenOptional;
     private final Optional<AppPairs> mAppPairsOptional;
     private final FullscreenTaskListener mFullscreenTaskListener;
     private final ShellExecutor mMainExecutor;
@@ -51,7 +52,7 @@
             DragAndDropController dragAndDropController,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<LegacySplitScreen> legacySplitScreenOptional,
-            Optional<SplitScreen> splitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             Optional<AppPairs> appPairsOptional,
             FullscreenTaskListener fullscreenTaskListener,
             Transitions transitions,
@@ -71,7 +72,7 @@
             DragAndDropController dragAndDropController,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<LegacySplitScreen> legacySplitScreenOptional,
-            Optional<SplitScreen> splitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             Optional<AppPairs> appPairsOptional,
             FullscreenTaskListener fullscreenTaskListener,
             Transitions transitions,
@@ -97,7 +98,7 @@
         mShellTaskOrganizer.registerOrganizer();
 
         mAppPairsOptional.ifPresent(AppPairs::onOrganizerRegistered);
-        mSplitScreenOptional.ifPresent(SplitScreen::onOrganizerRegistered);
+        mSplitScreenOptional.ifPresent(SplitScreenController::onOrganizerRegistered);
 
         // Bind the splitscreen impl to the drag drop controller
         mDragAndDropController.initialize(mSplitScreenOptional);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 616f24a..fb70cbe5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -40,9 +40,11 @@
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
+import android.view.SurfaceSession;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewRootImpl;
 import android.view.WindowManager;
 import android.view.WindowlessWindowManager;
 import android.window.ClientWindowFrames;
@@ -251,6 +253,8 @@
     public class SysUiWindowManager extends WindowlessWindowManager {
         final int mDisplayId;
         ContainerWindow mContainerWindow;
+        final HashMap<IBinder, SurfaceControl> mLeashForWindow =
+                new HashMap<IBinder, SurfaceControl>();
         public SysUiWindowManager(int displayId, Context ctx, SurfaceControl rootSurface,
                 ContainerWindow container) {
             super(ctx.getResources().getConfiguration(), rootSurface, null /* hostInputToken */);
@@ -263,7 +267,33 @@
         }
 
         SurfaceControl getSurfaceControlForWindow(View rootView) {
-            return getSurfaceControl(rootView);
+            synchronized (this) {
+                return mLeashForWindow.get(getWindowBinder(rootView));
+            }
+        }
+
+        protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+            SurfaceControl leash = new SurfaceControl.Builder(new SurfaceSession())
+                  .setContainerLayer()
+                  .setName("SystemWindowLeash")
+                  .setHidden(false)
+                  .setParent(mRootSurface)
+                  .setCallsite("SysUiWIndowManager#attachToParentSurface").build();
+            synchronized (this) {
+                mLeashForWindow.put(window.asBinder(), leash);
+            }
+            b.setParent(leash);
+        }
+
+        @Override
+        public void remove(android.view.IWindow window) throws RemoteException {
+            super.remove(window);
+            synchronized(this) {
+                IBinder token = window.asBinder();
+                new SurfaceControl.Transaction().remove(mLeashForWindow.get(token))
+                    .apply();
+                mLeashForWindow.remove(token);
+            }
         }
 
         void setTouchableRegionForWindow(View rootView, Region region) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 7f9c34f..728f60d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -87,7 +87,7 @@
     }
 
     @Override
-    protected void attachToParentSurface(SurfaceControl.Builder b) {
+    protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
         mParentContainerCallbacks.attachToParentSurface(b);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index c8938ad..1770943 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -53,6 +53,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.util.Optional;
 
@@ -66,7 +67,7 @@
 
     private final Context mContext;
     private final DisplayController mDisplayController;
-    private SplitScreen mSplitScreen;
+    private SplitScreenController mSplitScreen;
 
     private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
     private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
@@ -76,7 +77,7 @@
         mDisplayController = displayController;
     }
 
-    public void initialize(Optional<SplitScreen> splitscreen) {
+    public void initialize(Optional<SplitScreenController> splitscreen) {
         mSplitScreen = splitscreen.orElse(null);
         mDisplayController.addDisplayWindowListener(this);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 35dcdd5..6f5f2eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -64,7 +64,9 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreen.StagePosition;
+import com.android.wm.shell.splitscreen.SplitScreen.StageType;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -81,18 +83,18 @@
     private final Context mContext;
     private final ActivityTaskManager mActivityTaskManager;
     private final Starter mStarter;
-    private final SplitScreen mSplitScreen;
+    private final SplitScreenController mSplitScreen;
     private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>();
 
     private DragSession mSession;
 
-    public DragAndDropPolicy(Context context, SplitScreen splitScreen) {
+    public DragAndDropPolicy(Context context, SplitScreenController splitScreen) {
         this(context, ActivityTaskManager.getInstance(), splitScreen, new DefaultStarter(context));
     }
 
     @VisibleForTesting
     DragAndDropPolicy(Context context, ActivityTaskManager activityTaskManager,
-            SplitScreen splitScreen, Starter starter) {
+            SplitScreenController splitScreen, Starter starter) {
         mContext = context;
         mActivityTaskManager = activityTaskManager;
         mSplitScreen = splitScreen;
@@ -200,8 +202,8 @@
         final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
         final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
 
-        @SplitScreen.StageType int stage = STAGE_TYPE_UNDEFINED;
-        @SplitScreen.StagePosition int position = STAGE_POSITION_UNDEFINED;
+        @StageType int stage = STAGE_TYPE_UNDEFINED;
+        @StagePosition int position = STAGE_POSITION_UNDEFINED;
         if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) {
             // Update launch options for the split side we are targeting.
             position = leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -213,7 +215,28 @@
 
         final ClipDescription description = data.getDescription();
         final Intent dragData = mSession.dragData;
-        mStarter.startClipDescription(description, dragData, stage, position);
+        startClipDescription(description, dragData, stage, position);
+    }
+
+    private void startClipDescription(ClipDescription description, Intent intent,
+            @StageType int stage, @StagePosition int position) {
+        final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
+        final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
+        final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
+                ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle();
+
+        if (isTask) {
+            final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
+            mStarter.startTask(taskId, stage, position, opts);
+        } else if (isShortcut) {
+            final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+            final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
+            final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
+            mStarter.startShortcut(packageName, id, stage, position, opts, user);
+        } else {
+            mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position,
+                    opts);
+        }
     }
 
     /**
@@ -267,34 +290,13 @@
     /**
      * Interface for actually committing the task launches.
      */
-    @VisibleForTesting
     public interface Starter {
-        default void startClipDescription(ClipDescription description, Intent intent,
-                @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position) {
-            final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
-            final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
-            final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
-                    ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle();
-
-            if (isTask) {
-                final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
-                startTask(taskId, stage, position, opts);
-            } else if (isShortcut) {
-                final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
-                final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
-                final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
-                startShortcut(packageName, id, stage, position, opts, user);
-            } else {
-                startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position, opts);
-            }
-        }
-        void startTask(int taskId, @SplitScreen.StageType int stage,
-                @SplitScreen.StagePosition int position, @Nullable Bundle options);
-        void startShortcut(String packageName, String shortcutId,
-                @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
-                @Nullable Bundle options, UserHandle user);
-        void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
-                @SplitScreen.StagePosition int position, @Nullable Bundle options);
+        void startTask(int taskId, @StageType int stage, @StagePosition int position,
+                @Nullable Bundle options);
+        void startShortcut(String packageName, String shortcutId, @StageType int stage,
+                @StagePosition int position, @Nullable Bundle options, UserHandle user);
+        void startIntent(PendingIntent intent, @StageType int stage, @StagePosition int position,
+                @Nullable Bundle options);
         void enterSplitScreen(int taskId, boolean leftOrTop);
         void exitSplitScreen();
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index 82c4e44..b342336 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -42,7 +42,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.util.ArrayList;
 
@@ -61,7 +61,7 @@
     private boolean mIsShowing;
     private boolean mHasDropped;
 
-    public DragLayout(Context context, SplitScreen splitscreen) {
+    public DragLayout(Context context, SplitScreenController splitscreen) {
         super(context);
         mPolicy = new DragAndDropPolicy(context, splitscreen);
         mDisplayMargin = context.getResources().getDimensionPixelSize(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index e13a1db..a18d106 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -862,14 +862,6 @@
      * assigned to it.
      */
     private SurfaceControl getWindowSurfaceControl() {
-        final ViewRootImpl root = getViewRootImpl();
-        if (root == null) {
-            return null;
-        }
-        SurfaceControl out = root.getSurfaceControl();
-        if (out != null && out.isValid()) {
-            return out;
-        }
         return mWindowManager.mSystemWindows.getViewSurface(this);
     }
 
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 45aa387..5ffa988 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
@@ -16,6 +16,9 @@
 
 package com.android.wm.shell.pip;
 
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
 import android.animation.AnimationHandler;
 import android.animation.Animator;
 import android.animation.RectEvaluator;
@@ -24,11 +27,13 @@
 import android.app.TaskInfo;
 import android.graphics.Rect;
 import android.view.Choreographer;
+import android.view.Surface;
 import android.view.SurfaceControl;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.DisplayLayout;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -132,15 +137,21 @@
      * leash bounds before transformation/any animation. This is so when we try to construct
      * the different transformation matrices for the animation, we are constructing this based off
      * the PiP original bounds, rather than the {@param startBounds}, which is post-transformed.
+     *
+     * If non-zero {@param rotationDelta} is given, it means that the display will be rotated by
+     * leaving PiP to fullscreen, and the {@param endBounds} is the fullscreen bounds before the
+     * rotation change.
      */
     @VisibleForTesting
     public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
             Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect,
-            @PipAnimationController.TransitionDirection int direction, float startingAngle) {
+            @PipAnimationController.TransitionDirection int direction, float startingAngle,
+            @Surface.Rotation int rotationDelta) {
         if (mCurrentAnimator == null) {
             mCurrentAnimator = setupPipTransitionAnimator(
                     PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds,
-                            endBounds, sourceHintRect, direction, 0 /* startingAngle */));
+                            endBounds, sourceHintRect, direction, 0 /* startingAngle */,
+                            rotationDelta));
         } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
                 && mCurrentAnimator.isRunning()) {
             // If we are still animating the fade into pip, then just move the surface and ensure
@@ -156,7 +167,7 @@
             mCurrentAnimator.cancel();
             mCurrentAnimator = setupPipTransitionAnimator(
                     PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds,
-                            endBounds, sourceHintRect, direction, startingAngle));
+                            endBounds, sourceHintRect, direction, startingAngle, rotationDelta));
         }
         return mCurrentAnimator;
     }
@@ -410,7 +421,8 @@
 
         static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash,
                 Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
-                @PipAnimationController.TransitionDirection int direction, float startingAngle) {
+                @PipAnimationController.TransitionDirection int direction, float startingAngle,
+                @Surface.Rotation int rotationDelta) {
             // Just for simplicity we'll interpolate between the source rect hint insets and empty
             // insets to calculate the window crop
             final Rect initialSourceValue;
@@ -431,6 +443,16 @@
             }
             final Rect sourceInsets = new Rect(0, 0, 0, 0);
 
+            final Rect rotatedEndRect;
+            if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) {
+                // Rotate the end bounds according to the rotation delta because the display will
+                // be rotated to the same orientation.
+                rotatedEndRect = new Rect(endValue);
+                DisplayLayout.rotateBounds(rotatedEndRect, endValue, rotationDelta);
+            } else {
+                rotatedEndRect = null;
+            }
+
             // construct new Rect instances in case they are recycled
             return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS,
                     endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue),
@@ -444,6 +466,12 @@
                     final Rect base = getBaseValue();
                     final Rect start = getStartValue();
                     final Rect end = getEndValue();
+                    if (rotatedEndRect != null) {
+                        // Animate the bounds in a different orientation. It only happens when
+                        // leaving PiP to fullscreen.
+                        applyRotation(tx, leash, fraction, start, end, rotatedEndRect);
+                        return;
+                    }
                     Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
                     float angle = (1.0f - fraction) * startingAngle;
                     setCurrentValue(bounds);
@@ -469,6 +497,25 @@
                     tx.apply();
                 }
 
+                private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
+                        float fraction, Rect start, Rect end, Rect rotatedEndRect) {
+                    final Rect bounds = mRectEvaluator.evaluate(fraction, start, rotatedEndRect);
+                    setCurrentValue(bounds);
+                    final float degree, x, y;
+                    if (rotationDelta == ROTATION_90) {
+                        degree = 90 * fraction;
+                        x = fraction * (end.right - start.left) + start.left;
+                        y = fraction * (end.top - start.top) + start.top;
+                    } else {
+                        degree = -90 * fraction;
+                        x = fraction * (end.left - start.left) + start.left;
+                        y = fraction * (end.bottom - start.top) + start.top;
+                    }
+                    getSurfaceTransactionHelper().rotateAndScaleWithCrop(tx, leash, bounds,
+                            rotatedEndRect, degree, x, y);
+                    tx.apply();
+                }
+
                 @Override
                 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
                     getSurfaceTransactionHelper()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index a777a27..97aeda4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -127,6 +127,32 @@
     }
 
     /**
+     * Operates the rotation according to the given degrees and scale (setMatrix) according to the
+     * source bounds and rotated destination bounds. The crop will be the unscaled source bounds.
+     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+     */
+    public PipSurfaceTransactionHelper rotateAndScaleWithCrop(SurfaceControl.Transaction tx,
+            SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, float degrees,
+            float positionX, float positionY) {
+        mTmpDestinationRect.set(sourceBounds);
+        final int dw = destinationBounds.width();
+        final int dh = destinationBounds.height();
+        // Scale by the short side so there won't be empty area if the aspect ratio of source and
+        // destination are different.
+        final float scale = dw <= dh
+                ? (float) sourceBounds.width() / dw
+                : (float) sourceBounds.height() / dh;
+        // Inverse scale for crop to fit in screen coordinates.
+        mTmpDestinationRect.scale(1 / scale);
+        mTmpTransform.setRotate(degrees);
+        mTmpTransform.postScale(scale, scale);
+        mTmpTransform.postTranslate(positionX, positionY);
+        tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
+                .setWindowCrop(leash, mTmpDestinationRect.width(), mTmpDestinationRect.height());
+        return this;
+    }
+
+    /**
      * Resets the scale (setMatrix) on a given transaction and leash if there's any
      *
      * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
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 fb83006..ad6f435 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
@@ -48,13 +48,12 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Rational;
 import android.view.Display;
+import android.view.Surface;
 import android.view.SurfaceControl;
 import android.window.TaskOrganizer;
 import android.window.WindowContainerToken;
@@ -72,8 +71,6 @@
 import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Consumer;
@@ -134,7 +131,6 @@
     private final PipUiEventLogger mPipUiEventLoggerLogger;
     private final int mEnterExitAnimationDuration;
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
-    private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
     private final Optional<LegacySplitScreen> mSplitScreenOptional;
     protected final ShellTaskOrganizer mTaskOrganizer;
     protected final ShellExecutor mMainExecutor;
@@ -192,6 +188,12 @@
     private boolean mWaitForFixedRotation;
 
     /**
+     * The rotation that the display will apply after expanding PiP to fullscreen. This is only
+     * meaningful if {@link #mWaitForFixedRotation} is true.
+     */
+    private @Surface.Rotation int mNextRotation;
+
+    /**
      * If set to {@code true}, no entering PiP transition would be kicked off and most likely
      * it's due to the fact that Launcher is handling the transition directly when swiping
      * auto PiP-able Activity to home.
@@ -313,61 +315,40 @@
             return;
         }
 
-        final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
-        if (initialConfig == null) {
-            Log.wtf(TAG, "Token not in record, this should not happen mToken=" + mToken);
-            return;
-        }
         mPipUiEventLoggerLogger.log(
                 PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
-        final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
-                != mPipBoundsState.getDisplayLayout().rotation();
         final WindowContainerTransaction wct = new WindowContainerTransaction();
-        final Rect destinationBounds = initialConfig.windowConfiguration.getBounds();
+        final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
         final int direction = syncWithSplitScreenBounds(destinationBounds)
                 ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
                 : TRANSITION_DIRECTION_LEAVE_PIP;
-        if (orientationDiffers) {
-            mState = State.EXITING_PIP;
-            // Send started callback though animation is ignored.
-            sendOnPipTransitionStarted(direction);
-            // Don't bother doing an animation if the display rotation differs or if it's in
-            // a non-supported windowing mode
-            applyWindowingModeChangeOnExit(wct, direction);
-            mTaskOrganizer.applyTransaction(wct);
-            // Send finished callback though animation is ignored.
-            sendOnPipTransitionFinished(direction);
-        } else {
-            final SurfaceControl.Transaction tx =
-                    mSurfaceControlTransactionFactory.getTransaction();
-            mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
-                    mPipBoundsState.getBounds());
-            tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
-            // We set to fullscreen here for now, but later it will be set to UNDEFINED for
-            // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
-            wct.setActivityWindowingMode(mToken,
-                    direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
-                    ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                    : WINDOWING_MODE_FULLSCREEN);
-            wct.setBounds(mToken, destinationBounds);
-            wct.setBoundsChangeTransaction(mToken, tx);
-            mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
-                @Override
-                public void onTransactionReady(int id, SurfaceControl.Transaction t) {
-                    mMainExecutor.execute(() -> {
-                        t.apply();
-                        // Make sure to grab the latest source hint rect as it could have been
-                        // updated right after applying the windowing mode change.
-                        final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
-                                mPictureInPictureParams, destinationBounds);
-                        scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
-                                0 /* startingAngle */, sourceHintRect, direction,
-                                animationDurationMs, null /* updateBoundsCallback */);
-                        mState = State.EXITING_PIP;
-                    });
-                }
-            });
-        }
+        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+        mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds());
+        tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
+        // We set to fullscreen here for now, but later it will be set to UNDEFINED for
+        // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
+        wct.setActivityWindowingMode(mToken,
+                direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
+                        ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                        : WINDOWING_MODE_FULLSCREEN);
+        wct.setBounds(mToken, destinationBounds);
+        wct.setBoundsChangeTransaction(mToken, tx);
+        mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
+            @Override
+            public void onTransactionReady(int id, SurfaceControl.Transaction t) {
+                mMainExecutor.execute(() -> {
+                    t.apply();
+                    // Make sure to grab the latest source hint rect as it could have been
+                    // updated right after applying the windowing mode change.
+                    final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
+                            mPictureInPictureParams, destinationBounds);
+                    scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
+                            0 /* startingAngle */, sourceHintRect, direction,
+                            animationDurationMs, null /* updateBoundsCallback */);
+                    mState = State.EXITING_PIP;
+                });
+            }
+        });
     }
 
     private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) {
@@ -399,7 +380,6 @@
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(mEnterExitAnimationDuration)
                 .start();
-        mInitialState.remove(mToken.asBinder());
         mState = State.EXITING_PIP;
     }
 
@@ -424,7 +404,6 @@
         mToken = mTaskInfo.token;
         mState = State.TASK_APPEARED;
         mLeash = leash;
-        mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
         mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
         setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
                 mTaskInfo.topActivityInfo);
@@ -606,6 +585,7 @@
 
     @Override
     public void onFixedRotationStarted(int displayId, int newRotation) {
+        mNextRotation = newRotation;
         mWaitForFixedRotation = true;
     }
 
@@ -645,7 +625,7 @@
                 mPipAnimationController.getCurrentAnimator();
         if (animator == null || !animator.isRunning()
                 || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
-            if (mState.isInPip() && fromRotation) {
+            if (mState.isInPip() && fromRotation && !mWaitForFixedRotation) {
                 // Update bounds state to final destination first. It's important to do this
                 // before finishing & cancelling the transition animation so that the MotionHelper
                 // bounds are synchronized to the destination bounds when the animation ends.
@@ -1052,11 +1032,14 @@
             Log.w(TAG, "Abort animation, invalid leash");
             return;
         }
+        final int rotationDelta = mWaitForFixedRotation
+                ? ((mNextRotation - mPipBoundsState.getDisplayLayout().rotation()) + 4) % 4
+                : Surface.ROTATION_0;
         Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
                 ? mPipBoundsState.getBounds() : currentBounds;
         mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds,
-                        sourceHintRect, direction, startingAngle)
+                        sourceHintRect, direction, startingAngle, rotationDelta)
                 .setTransitionDirection(direction)
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(durationMs)
@@ -1101,11 +1084,6 @@
         pw.println(innerPrefix + "mState=" + mState);
         pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
         pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
-        pw.println(innerPrefix + "mInitialState:");
-        for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) {
-            pw.println(innerPrefix + "  binder=" + e.getKey()
-                    + " winConfig=" + e.getValue().windowConfiguration);
-        }
     }
 
     @Override
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 91e8c99..9b6909b 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
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.view.Surface;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
@@ -112,7 +113,7 @@
                             taskInfo.pictureInPictureParams, currentBounds);
             animator = mPipAnimationController.getAnimator(taskInfo, leash,
                     currentBounds, currentBounds, destinationBounds, sourceHintRect,
-                    TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */);
+                    TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
         } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
             t.setAlpha(leash, 0f);
             t.apply();
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 a57eee8..ae53005 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
@@ -198,8 +198,7 @@
      * {@code null}), it will get the leash that the WindowlessWM has assigned to it.
      */
     public SurfaceControl getSurfaceControl() {
-        SurfaceControl sf = mPipMenuView.getWindowSurfaceControl();
-        return sf != null ? sf : mSystemWindows.getViewSurface(mPipMenuView);
+        return mSystemWindows.getViewSurface(mPipMenuView);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
index 6d12752..3eeba6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
@@ -31,7 +31,6 @@
 
     private static final String TAG = "PipMenuIconsAlgorithm";
 
-    private boolean mFinishedLayout = false;
     protected ViewGroup mViewRoot;
     protected ViewGroup mTopEndContainer;
     protected View mDragHandle;
@@ -51,27 +50,16 @@
         mDragHandle = dragHandle;
         mSettingsButton = settingsButton;
         mDismissButton = dismissButton;
+
+        bindInitialViewState();
     }
 
     /**
      * Updates the position of the drag handle based on where the PIP window is on the screen.
      */
     public void onBoundsChanged(Rect bounds) {
-        if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
-                || mSettingsButton == null || mDismissButton == null) {
-            Log.e(TAG, "One if the required views is null.");
-            return;
-        }
-
-        //We only need to calculate the layout once since it does not change.
-        if (!mFinishedLayout) {
-            mTopEndContainer.removeView(mSettingsButton);
-            mViewRoot.addView(mSettingsButton);
-
-            setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
-            setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
-            mFinishedLayout = true;
-        }
+        // On phones, the menu icons are always static and will never move based on the PIP window
+        // position. No need to do anything here.
     }
 
     /**
@@ -84,4 +72,22 @@
             v.setLayoutParams(params);
         }
     }
+
+    /** Calculate the initial state of the menu icons. Called when the menu is first created. */
+    private void bindInitialViewState() {
+        if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
+                || mSettingsButton == null || mDismissButton == null) {
+            Log.e(TAG, "One of the required views is null.");
+            return;
+        }
+        // The menu view layout starts out with the settings button aligned at the top|end of the
+        // view group next to the dismiss button. On phones, the settings button should be aligned
+        // to the top|start of the view, so move it to parent view group to then align it to the
+        // top|start of the menu.
+        mTopEndContainer.removeView(mSettingsButton);
+        mViewRoot.addView(mSettingsButton);
+
+        setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
+        setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index b91ba34..53571ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -107,6 +107,7 @@
     private boolean mThresholdCrossed0;
     private boolean mThresholdCrossed1;
     private boolean mUsingPinchToZoom = false;
+    private float mAngle = 0;
     int mFirstIndex = -1;
     int mSecondIndex = -1;
 
@@ -420,18 +421,25 @@
                 float down1X = mDownSecondaryPoint.x;
                 float down1Y = mDownSecondaryPoint.y;
 
-                // Top right + Bottom left pinch to zoom.
-                if ((down0X > focusX && down0Y < focusY && down1X < focusX && down1Y > focusY)
-                        || (down1X > focusX && down1Y < focusY
-                        && down0X < focusX && down0Y > focusY)) {
+                if (down0X > focusX && down0Y < focusY && down1X < focusX && down1Y > focusY) {
+                    // Top right + Bottom left pinch to zoom.
                     mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
                             mLastResizeBounds.centerY(), x0, y0, x1, y1, true);
-                } else if ((down0X < focusX && down0Y < focusY
-                        && down1X > focusX && down1Y > focusY)
-                        || (down1X < focusX && down1Y < focusY
-                        && down0X > focusX && down0Y > focusY)) {
+                } else if (down1X > focusX && down1Y < focusY
+                        && down0X < focusX && down0Y > focusY) {
+                    // Top right + Bottom left pinch to zoom.
+                    mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
+                            mLastResizeBounds.centerY(), x1, y1, x0, y0, true);
+                } else if (down0X < focusX && down0Y < focusY
+                        && down1X > focusX && down1Y > focusY) {
+                    // Top left + bottom right pinch to zoom.
                     mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
                             mLastResizeBounds.centerY(), x0, y0, x1, y1, false);
+                } else if (down1X < focusX && down1Y < focusY
+                        && down0X > focusX && down0Y > focusY) {
+                    // Top left + bottom right pinch to zoom.
+                    mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
+                            mLastResizeBounds.centerY(), x1, y1, x0, y0, false);
                 }
 
                 mLastResizeBounds.set(PipPinchResizingAlgorithm.pinchResize(x0, y0, x1, y1,
@@ -445,21 +453,26 @@
         }
     }
 
-    private float mAngle = 0;
-
-    private float calculateRotationAngle(int focusX, int focusY, float x0, float y0,
-            float x1, float y1, boolean positive) {
+    private float calculateRotationAngle(int pivotX, int pivotY, float topX, float topY,
+            float bottomX, float bottomY, boolean positive) {
 
         // The base angle is the angle formed by taking the angle between the center horizontal
         // and one of the corners.
-        double baseAngle = Math.toDegrees(Math.atan2(Math.abs(mLastResizeBounds.top - focusY),
-                Math.abs(mLastResizeBounds.right - focusX)));
+        double baseAngle = Math.toDegrees(Math.atan2(Math.abs(mLastResizeBounds.top - pivotY),
+                Math.abs(mLastResizeBounds.right - pivotX)));
+
         double angle0 = mThresholdCrossed0
-                ? Math.toDegrees(Math.atan2(Math.abs(y0 - focusY), Math.abs(x0 - focusX)))
-                : baseAngle;
-        double angle1 = mThresholdCrossed1
-                ? Math.toDegrees(Math.atan2(Math.abs(y1 - focusY), Math.abs(x1 - focusX)))
-                : baseAngle;
+                ? Math.toDegrees(Math.atan2(pivotY - topY, topX - pivotX)) : baseAngle;
+        double angle1 = mThresholdCrossed0
+                ? Math.toDegrees(Math.atan2(pivotY - bottomY, bottomX - pivotX)) : baseAngle;
+
+
+        if (positive) {
+            angle1 = angle1 < 0 ? 180 + angle1 : angle1 - 180;
+        } else {
+            angle0 = angle0 < 0 ? -angle0 - 180 : 180 - angle0;
+            angle1 = -angle1;
+        }
 
         // Calculate the percentage difference of [0, 90] compare to the base angle.
         double diff0 = (Math.max(0, Math.min(angle0, 90)) - baseAngle) / 90;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 2c68092..177646b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -33,6 +33,7 @@
 
 /**
  * Interface to engage split-screen feature.
+ * TODO: Figure out which of these are actually needed outside of the Shell
  */
 @ExternalThread
 public interface SplitScreen extends DragAndDropPolicy.Starter {
@@ -102,18 +103,11 @@
     void setSideStagePosition(@StagePosition int sideStagePosition);
     /** Hides the side-stage if it is currently visible. */
     void setSideStageVisibility(boolean visible);
-    default void enterSplitScreen(int taskId, boolean leftOrTop) {
-        moveToSideStage(taskId,
-                leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
-    }
+
     /** Removes the split-screen stages. */
     void exitSplitScreen();
     /** Gets the stage bounds. */
     void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds);
-    /** Dumps current status of split-screen. */
-    void dump(@NonNull PrintWriter pw, String prefix);
-    /** Called when the shell organizer has been registered. */
-    void onOrganizerRegistered();
 
     void registerSplitScreenListener(SplitScreenListener listener);
     void unregisterSplitScreenListener(SplitScreenListener listener);
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 18dd53b..bbad36d 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
@@ -18,6 +18,13 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
+
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.PendingIntent;
@@ -35,7 +42,9 @@
 
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.draganddrop.DragAndDropPolicy;
 
 import java.io.PrintWriter;
 
@@ -44,25 +53,33 @@
  * {@link SplitScreen}.
  * @see StageCoordinator
  */
-public class SplitScreenController implements SplitScreen {
+public class SplitScreenController implements DragAndDropPolicy.Starter {
     private static final String TAG = SplitScreenController.class.getSimpleName();
 
     private final ShellTaskOrganizer mTaskOrganizer;
     private final SyncTransactionQueue mSyncQueue;
     private final Context mContext;
     private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
+    private final ShellExecutor mMainExecutor;
+    private final SplitScreenImpl mImpl = new SplitScreenImpl();
+
     private StageCoordinator mStageCoordinator;
 
     public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
             SyncTransactionQueue syncQueue, Context context,
-            RootTaskDisplayAreaOrganizer rootTDAOrganizer) {
+            RootTaskDisplayAreaOrganizer rootTDAOrganizer,
+            ShellExecutor mainExecutor) {
         mTaskOrganizer = shellTaskOrganizer;
         mSyncQueue = syncQueue;
         mContext = context;
         mRootTDAOrganizer = rootTDAOrganizer;
+        mMainExecutor = mainExecutor;
     }
 
-    @Override
+    public SplitScreen asSplitScreen() {
+        return mImpl;
+    }
+
     public void onOrganizerRegistered() {
         if (mStageCoordinator == null) {
             // TODO: Multi-display
@@ -71,13 +88,11 @@
         }
     }
 
-    @Override
     public boolean isSplitScreenVisible() {
         return mStageCoordinator.isSplitScreenVisible();
     }
 
-    @Override
-    public boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition) {
+    public boolean moveToSideStage(int taskId, @SplitScreen.StagePosition int sideStagePosition) {
         final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
         if (task == null) {
             throw new IllegalArgumentException("Unknown taskId" + taskId);
@@ -85,50 +100,46 @@
         return moveToSideStage(task, sideStagePosition);
     }
 
-    @Override
     public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
-            @StagePosition int sideStagePosition) {
+            @SplitScreen.StagePosition int sideStagePosition) {
         return mStageCoordinator.moveToSideStage(task, sideStagePosition);
     }
 
-    @Override
     public boolean removeFromSideStage(int taskId) {
         return mStageCoordinator.removeFromSideStage(taskId);
     }
 
-    @Override
-    public void setSideStagePosition(@StagePosition int sideStagePosition) {
+    public void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
         mStageCoordinator.setSideStagePosition(sideStagePosition);
     }
 
-    @Override
     public void setSideStageVisibility(boolean visible) {
         mStageCoordinator.setSideStageVisibility(visible);
     }
 
-    @Override
+    public void enterSplitScreen(int taskId, boolean leftOrTop) {
+        moveToSideStage(taskId,
+                leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
+    }
+
     public void exitSplitScreen() {
         mStageCoordinator.exitSplitScreen();
     }
 
-    @Override
     public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
         mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
     }
 
-    @Override
-    public void registerSplitScreenListener(SplitScreenListener listener) {
+    public void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
         mStageCoordinator.registerSplitScreenListener(listener);
     }
 
-    @Override
-    public void unregisterSplitScreenListener(SplitScreenListener listener) {
+    public void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) {
         mStageCoordinator.unregisterSplitScreenListener(listener);
     }
 
-    @Override
-    public void startTask(int taskId,
-            @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+    public void startTask(int taskId, @SplitScreen.StageType int stage,
+            @SplitScreen.StagePosition int position, @Nullable Bundle options) {
         options = resolveStartStage(stage, position, options);
 
         try {
@@ -138,9 +149,9 @@
         }
     }
 
-    @Override
-    public void startShortcut(String packageName, String shortcutId, @StageType int stage,
-            @StagePosition int position, @Nullable Bundle options, UserHandle user) {
+    public void startShortcut(String packageName, String shortcutId,
+            @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
+            @Nullable Bundle options, UserHandle user) {
         options = resolveStartStage(stage, position, options);
 
         try {
@@ -153,9 +164,8 @@
         }
     }
 
-    @Override
-    public void startIntent(PendingIntent intent,
-            @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+    public void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
+            @SplitScreen.StagePosition int position, @Nullable Bundle options) {
         options = resolveStartStage(stage, position, options);
 
         try {
@@ -165,8 +175,8 @@
         }
     }
 
-    private Bundle resolveStartStage(@StageType int stage, @StagePosition int position,
-            @Nullable Bundle options) {
+    private Bundle resolveStartStage(@SplitScreen.StageType int stage,
+            @SplitScreen.StagePosition int position, @Nullable Bundle options) {
         switch (stage) {
             case STAGE_TYPE_UNDEFINED: {
                 // Use the stage of the specified position is valid.
@@ -216,7 +226,6 @@
         return options;
     }
 
-    @Override
     public void dump(@NonNull PrintWriter pw, String prefix) {
         pw.println(prefix + TAG);
         if (mStageCoordinator != null) {
@@ -224,4 +233,113 @@
         }
     }
 
+    private class SplitScreenImpl implements SplitScreen {
+        @Override
+        public boolean isSplitScreenVisible() {
+            return mMainExecutor.executeBlockingForResult(() -> {
+                return SplitScreenController.this.isSplitScreenVisible();
+            }, Boolean.class);
+        }
+
+        @Override
+        public boolean moveToSideStage(int taskId, int sideStagePosition) {
+            return mMainExecutor.executeBlockingForResult(() -> {
+                return SplitScreenController.this.moveToSideStage(taskId, sideStagePosition);
+            }, Boolean.class);
+        }
+
+        @Override
+        public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+                int sideStagePosition) {
+            return mMainExecutor.executeBlockingForResult(() -> {
+                return SplitScreenController.this.moveToSideStage(task, sideStagePosition);
+            }, Boolean.class);
+        }
+
+        @Override
+        public boolean removeFromSideStage(int taskId) {
+            return mMainExecutor.executeBlockingForResult(() -> {
+                return SplitScreenController.this.removeFromSideStage(taskId);
+            }, Boolean.class);
+        }
+
+        @Override
+        public void setSideStagePosition(int sideStagePosition) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.setSideStagePosition(sideStagePosition);
+            });
+        }
+
+        @Override
+        public void setSideStageVisibility(boolean visible) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.setSideStageVisibility(visible);
+            });
+        }
+
+        @Override
+        public void enterSplitScreen(int taskId, boolean leftOrTop) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.enterSplitScreen(taskId, leftOrTop);
+            });
+        }
+
+        @Override
+        public void exitSplitScreen() {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.exitSplitScreen();
+            });
+        }
+
+        @Override
+        public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
+            try {
+                mMainExecutor.executeBlocking(() -> {
+                    SplitScreenController.this.getStageBounds(outTopOrLeftBounds,
+                            outBottomOrRightBounds);
+                });
+            } catch (InterruptedException e) {
+                Slog.e(TAG, "Failed to get stage bounds in 2s");
+            }
+        }
+
+        @Override
+        public void registerSplitScreenListener(SplitScreenListener listener) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.registerSplitScreenListener(listener);
+            });
+        }
+
+        @Override
+        public void unregisterSplitScreenListener(SplitScreenListener listener) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.unregisterSplitScreenListener(listener);
+            });
+        }
+
+        @Override
+        public void startTask(int taskId, int stage, int position, @Nullable Bundle options) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.startTask(taskId, stage, position, options);
+            });
+        }
+
+        @Override
+        public void startShortcut(String packageName, String shortcutId, int stage, int position,
+                @Nullable Bundle options, UserHandle user) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.startShortcut(packageName, shortcutId, stage, position,
+                        options, user);
+            });
+        }
+
+        @Override
+        public void startIntent(PendingIntent intent, int stage, int position,
+                @Nullable Bundle options) {
+            mMainExecutor.execute(() -> {
+                SplitScreenController.this.startIntent(intent, stage, position, options);
+            });
+        }
+    }
+
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 8e24e0b..f374922 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -377,12 +377,10 @@
         final int taskId = startingWindowInfo.taskInfo.taskId;
         final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
                 snapshot, mMainExecutor, () -> removeWindowSynced(taskId) /* clearWindow */);
-        mMainExecutor.execute(() -> {
-            mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
-            final StartingWindowRecord tView =
-                    new StartingWindowRecord(null/* decorView */, surface);
-            mStartingWindowRecords.put(taskId, tView);
-        });
+        mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
+        final StartingWindowRecord tView =
+                new StartingWindowRecord(null/* decorView */, surface);
+        mStartingWindowRecords.put(taskId, tView);
     }
 
     /**
@@ -392,42 +390,40 @@
         if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
             Slog.d(TAG, "Task start finish, remove starting surface for task " + taskId);
         }
-        mMainExecutor.execute(() -> removeWindowSynced(taskId));
+        removeWindowSynced(taskId);
     }
 
     protected void postAddWindow(int taskId, IBinder appToken,
-            View view, WindowManager wm, WindowManager.LayoutParams params) {
-        mMainExecutor.execute(() -> {
-            boolean shouldSaveView = true;
-            try {
-                wm.addView(view, params);
-            } catch (WindowManager.BadTokenException e) {
-                // ignore
-                Slog.w(TAG, appToken + " already running, starting window not displayed. "
-                        + e.getMessage());
+        View view, WindowManager wm, WindowManager.LayoutParams params) {
+        boolean shouldSaveView = true;
+        try {
+            wm.addView(view, params);
+        } catch (WindowManager.BadTokenException e) {
+            // ignore
+            Slog.w(TAG, appToken + " already running, starting window not displayed. "
+                    + e.getMessage());
+            shouldSaveView = false;
+        } catch (RuntimeException e) {
+            // don't crash if something else bad happens, for example a
+            // failure loading resources because we are loading from an app
+            // on external storage that has been unmounted.
+            Slog.w(TAG, appToken + " failed creating starting window", e);
+            shouldSaveView = false;
+        } finally {
+            if (view != null && view.getParent() == null) {
+                Slog.w(TAG, "view not successfully added to wm, removing view");
+                wm.removeViewImmediate(view);
                 shouldSaveView = false;
-            } catch (RuntimeException e) {
-                // don't crash if something else bad happens, for example a
-                // failure loading resources because we are loading from an app
-                // on external storage that has been unmounted.
-                Slog.w(TAG, appToken + " failed creating starting window", e);
-                shouldSaveView = false;
-            } finally {
-                if (view != null && view.getParent() == null) {
-                    Slog.w(TAG, "view not successfully added to wm, removing view");
-                    wm.removeViewImmediate(view);
-                    shouldSaveView = false;
-                }
             }
+        }
 
-            if (shouldSaveView) {
-                removeWindowSynced(taskId);
-                mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
-                final StartingWindowRecord tView =
-                        new StartingWindowRecord(view, null /* TaskSnapshotWindow */);
-                mStartingWindowRecords.put(taskId, tView);
-            }
-        });
+        if (shouldSaveView) {
+            removeWindowSynced(taskId);
+            mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
+            final StartingWindowRecord tView =
+                    new StartingWindowRecord(view, null /* TaskSnapshotWindow */);
+            mStartingWindowRecords.put(taskId, tView);
+        }
     }
 
     protected void removeWindowSynced(int taskId) {
@@ -445,7 +441,7 @@
                 if (DEBUG_TASK_SNAPSHOT) {
                     Slog.v(TAG, "Removing task snapshot window for " + taskId);
                 }
-                record.mTaskSnapshotWindow.remove(mMainExecutor);
+                record.mTaskSnapshotWindow.remove();
             }
             mStartingWindowRecords.remove(taskId);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index b5e1896..a6f44ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -82,6 +82,7 @@
 import com.android.internal.policy.DecorView;
 import com.android.internal.view.BaseIWindow;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ExternalThread;
 
 /**
  * This class represents a starting window that shows a snapshot.
@@ -121,6 +122,7 @@
     private final Window mWindow;
     private final Surface mSurface;
     private final Runnable mClearWindowHandler;
+    private final ShellExecutor mMainExecutor;
     private SurfaceControl mSurfaceControl;
     private SurfaceControl mChildSurfaceControl;
     private final IWindowSession mSession;
@@ -213,7 +215,7 @@
         final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
                 surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance,
                 windowFlags, windowPrivateFlags, taskBounds, orientation, activityType,
-                topWindowInsetsState, clearWindowHandler);
+                topWindowInsetsState, clearWindowHandler, mainExecutor);
         final Window window = snapshotSurface.mWindow;
 
         final InsetsState mTmpInsetsState = new InsetsState();
@@ -229,7 +231,7 @@
             } catch (RemoteException e) {
                 snapshotSurface.clearWindowSynced();
             }
-            window.setOuter(snapshotSurface, mainExecutor);
+            window.setOuter(snapshotSurface);
             try {
                 session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
                         tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
@@ -249,7 +251,8 @@
             TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
             int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds,
             int currentOrientation, int activityType, InsetsState topWindowInsetsState,
-            Runnable clearWindowHandler) {
+            Runnable clearWindowHandler, ShellExecutor mainExecutor) {
+        mMainExecutor = mainExecutor;
         mSurface = new Surface();
         mSession = WindowManagerGlobal.getWindowSession();
         mWindow = new Window();
@@ -286,28 +289,26 @@
         mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
     }
 
-    void remove(ShellExecutor mainExecutor) {
+    void remove() {
         final long now = SystemClock.uptimeMillis();
         if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS
                 // Show the latest content as soon as possible for unlocking to home.
                 && mActivityType != ACTIVITY_TYPE_HOME) {
             final long delayTime = mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS - now;
-            mainExecutor.executeDelayed(() -> remove(mainExecutor), delayTime);
+            mMainExecutor.executeDelayed(() -> remove(), delayTime);
             if (DEBUG) {
                 Slog.d(TAG, "Defer removing snapshot surface in " + delayTime);
             }
             return;
         }
-        mainExecutor.execute(() -> {
-            try {
-                if (DEBUG) {
-                    Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn);
-                }
-                mSession.remove(mWindow);
-            } catch (RemoteException e) {
-                // nothing
+        try {
+            if (DEBUG) {
+                Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn);
             }
-        });
+            mSession.remove(mWindow);
+        } catch (RemoteException e) {
+            // nothing
+        }
     }
 
     /**
@@ -497,13 +498,12 @@
         }
     }
 
+    @ExternalThread
     static class Window extends BaseIWindow {
         private TaskSnapshotWindow mOuter;
-        private ShellExecutor mMainExecutor;
 
-        public void setOuter(TaskSnapshotWindow outer, ShellExecutor mainExecutor) {
+        public void setOuter(TaskSnapshotWindow outer) {
             mOuter = outer;
-            mMainExecutor = mainExecutor;
         }
 
         @Override
@@ -511,22 +511,20 @@
                 MergedConfiguration mergedConfiguration, boolean forceLayout,
                 boolean alwaysConsumeSystemBars, int displayId) {
             if (mOuter != null) {
-                if (mergedConfiguration != null
-                        && mOuter.mOrientationOnCreation
-                        != mergedConfiguration.getMergedConfiguration().orientation) {
-                    // The orientation of the screen is changing. We better remove the snapshot ASAP
-                    // as we are going to wait on the new window in any case to unfreeze the screen,
-                    // and the starting window is not needed anymore.
-                    mMainExecutor.execute(() -> {
+                mOuter.mMainExecutor.execute(() -> {
+                    if (mergedConfiguration != null
+                            && mOuter.mOrientationOnCreation
+                            != mergedConfiguration.getMergedConfiguration().orientation) {
+                        // The orientation of the screen is changing. We better remove the snapshot
+                        // ASAP as we are going to wait on the new window in any case to unfreeze
+                        // the screen, and the starting window is not needed anymore.
                         mOuter.clearWindowSynced();
-                    });
-                } else if (reportDraw) {
-                    mMainExecutor.execute(() -> {
+                    } else if (reportDraw) {
                         if (mOuter.mHasDrawn) {
                             mOuter.reportDrawn();
                         }
-                    });
-                }
+                    }
+                });
             }
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java
new file mode 100644
index 0000000..85bbf74
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition;
+
+import android.annotation.NonNull;
+import android.window.IRemoteTransition;
+import android.window.TransitionFilter;
+
+import com.android.wm.shell.common.annotations.ExternalThread;
+
+/**
+ * Interface to manage remote transitions.
+ */
+@ExternalThread
+public interface RemoteTransitions {
+    /**
+     * Registers a remote transition.
+     */
+    void registerRemote(@NonNull TransitionFilter filter,
+            @NonNull IRemoteTransition remoteTransition);
+
+    /**
+     * Unregisters a remote transition.
+     */
+    void unregisterRemote(@NonNull IRemoteTransition remoteTransition);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index d8687bd..1034489 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -67,6 +67,7 @@
     private final ShellExecutor mAnimExecutor;
     private final TransitionPlayerImpl mPlayerImpl;
     private final RemoteTransitionHandler mRemoteTransitionHandler;
+    private final RemoteTransitionImpl mImpl = new RemoteTransitionImpl();
 
     /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
     private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
@@ -78,6 +79,10 @@
     /** Keeps track of currently tracked transitions and all the animations associated with each */
     private final ArrayMap<IBinder, ActiveTransition> mActiveTransitions = new ArrayMap<>();
 
+    public static RemoteTransitions asRemoteTransitions(Transitions transitions) {
+        return transitions.mImpl;
+    }
+
     public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
             @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
         mOrganizer = organizer;
@@ -101,8 +106,20 @@
 
     /** Create an empty/non-registering transitions object for system-ui tests. */
     @VisibleForTesting
-    public static Transitions createEmptyForTesting() {
-        return new Transitions();
+    public static RemoteTransitions createEmptyForTesting() {
+        return new RemoteTransitions() {
+            @Override
+            public void registerRemote(@androidx.annotation.NonNull TransitionFilter filter,
+                    @androidx.annotation.NonNull IRemoteTransition remoteTransition) {
+                // Do nothing
+            }
+
+            @Override
+            public void unregisterRemote(
+                    @androidx.annotation.NonNull IRemoteTransition remoteTransition) {
+                // Do nothing
+            }
+        };
     }
 
     /** Register this transition handler with Core */
@@ -134,16 +151,14 @@
     }
 
     /** Register a remote transition to be used when `filter` matches an incoming transition */
-    @ExternalThread
     public void registerRemote(@NonNull TransitionFilter filter,
             @NonNull IRemoteTransition remoteTransition) {
-        mMainExecutor.execute(() -> mRemoteTransitionHandler.addFiltered(filter, remoteTransition));
+        mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
     }
 
     /** Unregisters a remote transition and all associated filters */
-    @ExternalThread
     public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
-        mMainExecutor.execute(() -> mRemoteTransitionHandler.removeFiltered(remoteTransition));
+        mRemoteTransitionHandler.removeFiltered(remoteTransition);
     }
 
     /** @return true if the transition was triggered by opening something vs closing something */
@@ -371,4 +386,22 @@
             mMainExecutor.execute(() -> Transitions.this.requestStartTransition(iBinder, request));
         }
     }
+
+    @ExternalThread
+    private class RemoteTransitionImpl implements RemoteTransitions {
+        @Override
+        public void registerRemote(@NonNull TransitionFilter filter,
+                @NonNull IRemoteTransition remoteTransition) {
+            mMainExecutor.execute(() -> {
+                Transitions.this.registerRemote(filter, remoteTransition);
+            });
+        }
+
+        @Override
+        public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
+            mMainExecutor.execute(() -> {
+                Transitions.this.unregisterRemote(remoteTransition);
+            });
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index 8258630..f06d57c 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -25,8 +25,6 @@
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.wm.shell.flicker"/>
-        <option name="include-annotation" value="androidx.test.filters.RequiresDevice" />
-        <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
         <option name="shell-timeout" value="6600s" />
         <option name="test-timeout" value="6000s" />
         <option name="hidden-api-checks" value="false" />
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 7ad7553..3282ece 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -18,6 +18,7 @@
 
 import android.graphics.Region
 import android.view.Surface
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
 import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
 import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
 import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
@@ -25,6 +26,109 @@
 import com.android.server.wm.flicker.traces.layers.getVisibleBounds
 
 @JvmOverloads
+fun LayersAssertionBuilder.appPairsDividerIsVisible(bugId: Int = 0) {
+    end("appPairsDividerIsVisible", bugId) {
+        this.isVisible(APP_PAIR_SPLIT_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsDividerIsInvisible(bugId: Int = 0) {
+    end("appPairsDividerIsInVisible", bugId) {
+        this.notExists(APP_PAIR_SPLIT_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsDividerBecomesVisible(bugId: Int = 0) {
+    all("dividerLayerBecomesVisible", bugId) {
+        this.hidesLayer(DOCKED_STACK_DIVIDER)
+            .then()
+            .showsLayer(DOCKED_STACK_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerIsVisible(bugId: Int = 0) {
+    end("dockedStackDividerIsVisible", bugId) {
+        this.isVisible(DOCKED_STACK_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerBecomesVisible(bugId: Int = 0) {
+    all("dividerLayerBecomesVisible", bugId) {
+        this.hidesLayer(DOCKED_STACK_DIVIDER)
+            .then()
+            .showsLayer(DOCKED_STACK_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerBecomesInvisible(bugId: Int = 0) {
+    all("dividerLayerBecomesInvisible", bugId) {
+        this.showsLayer(DOCKED_STACK_DIVIDER)
+            .then()
+            .hidesLayer(DOCKED_STACK_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerIsInvisible(bugId: Int = 0) {
+    end("dockedStackDividerIsInvisible", bugId) {
+        this.notExists(DOCKED_STACK_DIVIDER)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsPrimaryBoundsIsVisible(
+    rotation: Int,
+    primaryLayerName: String,
+    bugId: Int = 0
+) {
+    end("PrimaryAppBounds", bugId) {
+        val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+        this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsSecondaryBoundsIsVisible(
+    rotation: Int,
+    secondaryLayerName: String,
+    bugId: Int = 0
+) {
+    end("SecondaryAppBounds", bugId) {
+        val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+        this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackPrimaryBoundsIsVisible(
+    rotation: Int,
+    primaryLayerName: String,
+    bugId: Int = 0
+) {
+    end("PrimaryAppBounds", bugId) {
+        val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
+        this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackSecondaryBoundsIsVisible(
+    rotation: Int,
+    secondaryLayerName: String,
+    bugId: Int = 0
+) {
+    end("SecondaryAppBounds", bugId) {
+        val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
+        this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
+    }
+}
+
+@JvmOverloads
 fun LayersAssertionBuilderLegacy.appPairsDividerIsVisible(
     bugId: Int = 0,
     enabled: Boolean = bugId == 0
@@ -51,8 +155,8 @@
 ) {
     all("dividerLayerBecomesVisible", bugId, enabled) {
         this.hidesLayer(DOCKED_STACK_DIVIDER)
-                .then()
-                .showsLayer(DOCKED_STACK_DIVIDER)
+            .then()
+            .showsLayer(DOCKED_STACK_DIVIDER)
     }
 }
 
@@ -73,8 +177,8 @@
 ) {
     all("dividerLayerBecomesVisible", bugId, enabled) {
         this.hidesLayer(DOCKED_STACK_DIVIDER)
-                .then()
-                .showsLayer(DOCKED_STACK_DIVIDER)
+            .then()
+            .showsLayer(DOCKED_STACK_DIVIDER)
     }
 }
 
@@ -85,8 +189,8 @@
 ) {
     all("dividerLayerBecomesInvisible", bugId, enabled) {
         this.showsLayer(DOCKED_STACK_DIVIDER)
-                .then()
-                .hidesLayer(DOCKED_STACK_DIVIDER)
+            .then()
+            .hidesLayer(DOCKED_STACK_DIVIDER)
     }
 }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index d257749..c3fd663 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -17,7 +17,6 @@
 package com.android.wm.shell.flicker.apppairs
 
 import android.os.Bundle
-import android.platform.test.annotations.Presubmit
 import android.os.SystemClock
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -40,7 +39,6 @@
  * Test cold launch app from launcher.
  * To run this test: `atest WMShellFlickerTests:AppPairsTestCannotPairNonResizeableApps`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -51,10 +49,9 @@
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): List<Array<Any>> {
-            val testTag = "testAppPairs_cannotPairNonResizeableApps"
             val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
                 withTestName {
-                    buildTestTag(testTag, configuration)
+                    buildTestTag(configuration)
                 }
                 transitions {
                     nonResizeableApp?.launchViaIntent(wmHelper)
@@ -64,17 +61,20 @@
                     SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
                 }
                 assertions {
-                    layersTrace {
-                        appPairsDividerIsInvisible()
-                    }
-                    windowManagerTrace {
-                        end("onlyResizeableAppWindowVisible") {
+                    presubmit {
+                        layersTrace {
+                            appPairsDividerIsInvisible()
+                        }
+                        windowManagerTrace {
                             val nonResizeableApp = nonResizeableApp
                             require(nonResizeableApp != null) {
                                 "Non resizeable app not initialized"
                             }
-                            isVisible(nonResizeableApp.defaultWindowName)
-                            isInvisible(primaryApp.defaultWindowName)
+
+                            end("onlyResizeableAppWindowVisible") {
+                                isVisible(nonResizeableApp.defaultWindowName)
+                                isInvisible(primaryApp.defaultWindowName)
+                            }
                         }
                     }
                 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 257350b..7a2a5e4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -18,7 +18,6 @@
 
 import android.os.Bundle
 import android.os.SystemClock
-import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
@@ -38,7 +37,6 @@
  * Test cold launch app from launcher.
  * To run this test: `atest WMShellFlickerTests:AppPairsTestPairPrimaryAndSecondaryApps`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -49,10 +47,9 @@
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): List<Array<Any>> {
-            val testTag = "testAppPairs_pairPrimaryAndSecondaryApps"
             val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
                 withTestName {
-                    buildTestTag(testTag, configuration)
+                    buildTestTag(configuration)
                 }
                 transitions {
                     // TODO pair apps through normal UX flow
@@ -61,20 +58,27 @@
                     SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
                 }
                 assertions {
-                    layersTrace {
-                        appPairsDividerIsVisible()
-                        end("appsEndingBounds", enabled = false) {
-                            val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-                            this.hasVisibleRegion(primaryApp.defaultWindowName,
-                                appPairsHelper.getPrimaryBounds(dividerRegion))
-                                .hasVisibleRegion(secondaryApp.defaultWindowName,
-                                    appPairsHelper.getSecondaryBounds(dividerRegion))
+                    presubmit {
+                        layersTrace {
+                            appPairsDividerIsVisible()
+                        }
+                        windowManagerTrace {
+                            end("bothAppWindowsVisible") {
+                                isVisible(primaryApp.defaultWindowName)
+                                isVisible(secondaryApp.defaultWindowName)
+                            }
                         }
                     }
-                    windowManagerTrace {
-                        end("bothAppWindowsVisible") {
-                            isVisible(primaryApp.defaultWindowName)
-                            isVisible(secondaryApp.defaultWindowName)
+
+                    flaky {
+                        layersTrace {
+                            end("appsEndingBounds") {
+                                val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+                                this.hasVisibleRegion(primaryApp.defaultWindowName,
+                                    appPairsHelper.getPrimaryBounds(dividerRegion))
+                                    .hasVisibleRegion(secondaryApp.defaultWindowName,
+                                        appPairsHelper.getSecondaryBounds(dividerRegion))
+                            }
                         }
                     }
                 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 0b001f5..d8dc4c2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -18,7 +18,6 @@
 
 import android.os.Bundle
 import android.os.SystemClock
-import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
@@ -38,7 +37,6 @@
  * Test cold launch app from launcher.
  * To run this test: `atest WMShellFlickerTests:AppPairsTestUnpairPrimaryAndSecondaryApps`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -49,10 +47,9 @@
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): List<Array<Any>> {
-            val testTag = "testAppPairs_unpairPrimaryAndSecondaryApps"
             val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
                 withTestName {
-                    buildTestTag(testTag, configuration)
+                    buildTestTag(configuration)
                 }
                 setup {
                     executeShellCommand(
@@ -66,24 +63,31 @@
                     SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
                 }
                 assertions {
-                    layersTrace {
-                        appPairsDividerIsInvisible()
-                        start("appsStartingBounds", enabled = false) {
-                            val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-                            this.hasVisibleRegion(primaryApp.defaultWindowName,
-                                appPairsHelper.getPrimaryBounds(dividerRegion))
-                                .hasVisibleRegion(secondaryApp.defaultWindowName,
-                                    appPairsHelper.getSecondaryBounds(dividerRegion))
+                    presubmit {
+                        layersTrace {
+                            appPairsDividerIsInvisible()
                         }
-                        end("appsEndingBounds", enabled = false) {
-                            this.notExists(primaryApp.defaultWindowName)
-                                .notExists(secondaryApp.defaultWindowName)
+                        windowManagerTrace {
+                            end("bothAppWindowsInvisible") {
+                                isInvisible(primaryApp.defaultWindowName)
+                                isInvisible(secondaryApp.defaultWindowName)
+                            }
                         }
                     }
-                    windowManagerTrace {
-                        end("bothAppWindowsInvisible") {
-                            isInvisible(primaryApp.defaultWindowName)
-                            isInvisible(secondaryApp.defaultWindowName)
+
+                    flaky {
+                        layersTrace {
+                            start("appsStartingBounds") {
+                                val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+                                this.hasVisibleRegion(primaryApp.defaultWindowName,
+                                    appPairsHelper.getPrimaryBounds(dividerRegion))
+                                    .hasVisibleRegion(secondaryApp.defaultWindowName,
+                                        appPairsHelper.getSecondaryBounds(dividerRegion))
+                            }
+                            end("appsEndingBounds") {
+                                this.notExists(primaryApp.defaultWindowName)
+                                    .notExists(secondaryApp.defaultWindowName)
+                            }
                         }
                     }
                 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index aafa9bf..8aee005 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -18,7 +18,6 @@
 
 import android.os.Bundle
 import android.os.SystemClock
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -46,7 +45,6 @@
  * Test open apps to app pairs and rotate.
  * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsInAppPairsMode`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -60,7 +58,7 @@
         fun getParams(): Collection<Array<Any>> {
             val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
                 withTestName {
-                    buildTestTag("testRotateTwoLaunchedAppsInAppPairsMode", configuration)
+                    buildTestTag(configuration)
                 }
                 transitions {
                     executeShellCommand(composePairsCommand(
@@ -69,23 +67,28 @@
                     setRotation(configuration.endRotation)
                 }
                 assertions {
-                    layersTrace {
-                        navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation,
-                            enabled = false)
-                        statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation,
-                            enabled = false)
-                        appPairsDividerIsVisible(enabled = false)
-                        appPairsPrimaryBoundsIsVisible(configuration.endRotation,
-                            primaryApp.defaultWindowName, bugId = 172776659)
-                        appPairsSecondaryBoundsIsVisible(configuration.endRotation,
-                            secondaryApp.defaultWindowName, bugId = 172776659)
+                    presubmit {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            end("bothAppWindowsVisible") {
+                                isVisible(primaryApp.defaultWindowName)
+                                    .isVisible(secondaryApp.defaultWindowName)
+                            }
+                        }
                     }
-                    windowManagerTrace {
-                        navBarWindowIsAlwaysVisible()
-                        statusBarWindowIsAlwaysVisible()
-                        end("bothAppWindowsVisible") {
-                            isVisible(primaryApp.defaultWindowName)
-                                .isVisible(secondaryApp.defaultWindowName)
+
+                    flaky {
+                        layersTrace {
+                            appPairsDividerIsVisible()
+                            navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            appPairsPrimaryBoundsIsVisible(configuration.endRotation,
+                                primaryApp.defaultWindowName, bugId = 172776659)
+                            appPairsSecondaryBoundsIsVisible(configuration.endRotation,
+                                secondaryApp.defaultWindowName, bugId = 172776659)
                         }
                     }
                 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index 19ca31f..bc99c94 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -62,7 +62,7 @@
         fun getParams(): Collection<Array<Any>> {
             val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
                 withTestName {
-                    buildTestTag("testRotateAndEnterAppPairsMode", configuration)
+                    buildTestTag(configuration)
                 }
                 transitions {
                     this.setRotation(configuration.endRotation)
@@ -71,22 +71,37 @@
                     SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
                 }
                 assertions {
-                    layersTrace {
-                        navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation,
-                            enabled = !configuration.startRotation.isRotated())
-                        statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation)
-                        appPairsDividerIsVisible()
-                        appPairsPrimaryBoundsIsVisible(configuration.endRotation,
-                            primaryApp.defaultWindowName, 172776659)
-                        appPairsSecondaryBoundsIsVisible(configuration.endRotation,
-                            secondaryApp.defaultWindowName, 172776659)
+                    val isRotated = configuration.startRotation.isRotated()
+                    presubmit {
+                        layersTrace {
+                            statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            appPairsDividerIsVisible()
+                            if (!isRotated) {
+                                navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                    configuration.endRotation)
+                            }
+                        }
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            end("bothAppWindowsVisible") {
+                                isVisible(primaryApp.defaultWindowName)
+                                isVisible(secondaryApp.defaultWindowName)
+                            }
+                        }
                     }
-                    windowManagerTrace {
-                        navBarWindowIsAlwaysVisible()
-                        statusBarWindowIsAlwaysVisible()
-                        end("bothAppWindowsVisible") {
-                            isVisible(primaryApp.defaultWindowName)
-                            isVisible(secondaryApp.defaultWindowName)
+                    flaky {
+                        layersTrace {
+                            appPairsPrimaryBoundsIsVisible(configuration.endRotation,
+                                primaryApp.defaultWindowName, 172776659)
+                            appPairsSecondaryBoundsIsVisible(configuration.endRotation,
+                                secondaryApp.defaultWindowName, 172776659)
+
+                            if (isRotated) {
+                                navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                    configuration.endRotation)
+                            }
                         }
                     }
                 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 2572106..19ecc49 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -44,6 +44,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
@@ -65,7 +66,7 @@
 
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -92,7 +93,7 @@
 
     // Both the split-screen and start interface.
     @Mock
-    private SplitScreen mSplitScreenStarter;
+    private SplitScreenController mSplitScreenStarter;
 
     private DisplayLayout mLandscapeDisplayLayout;
     private DisplayLayout mPortraitDisplayLayout;
@@ -127,8 +128,8 @@
         mPortraitDisplayLayout = new DisplayLayout(info2, res, false, false);
         mInsets = Insets.of(0, 0, 0, 0);
 
-        mPolicy = new DragAndDropPolicy(
-                mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter);
+        mPolicy = spy(new DragAndDropPolicy(
+                mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter));
         mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
         mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
         setClipDataResizeable(mNonResizeableActivityClipData, false);
@@ -204,8 +205,8 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
     }
 
     @Test
@@ -216,13 +217,13 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
         reset(mSplitScreenStarter);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
     @Test
@@ -233,13 +234,13 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
         reset(mSplitScreenStarter);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
     @Test
@@ -250,8 +251,8 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
     }
 
     @Test
@@ -262,8 +263,8 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
     }
 
     @Test
@@ -275,14 +276,14 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
         reset(mSplitScreenStarter);
 
         // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
     @Test
@@ -294,14 +295,14 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
         reset(mSplitScreenStarter);
 
         // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
-        verify(mSplitScreenStarter).startClipDescription(any(), any(),
-                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+        verify(mSplitScreenStarter).startIntent(any(),
+                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 0087d91..3147dab 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.wm.shell.pip;
 
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
+
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
 
@@ -34,6 +37,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -84,7 +88,7 @@
     public void getAnimator_withBounds_returnBoundsAnimator() {
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, new Rect(), new Rect(), new Rect(), null,
-                        TRANSITION_DIRECTION_TO_PIP, 0);
+                        TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
 
         assertEquals("Expect ANIM_TYPE_BOUNDS animation",
                 animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -98,13 +102,13 @@
         final Rect endValue2 = new Rect(200, 200, 300, 300);
         final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
-                        TRANSITION_DIRECTION_TO_PIP, 0);
+                        TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
         oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
         oldAnimator.start();
 
         final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue2, null,
-                        TRANSITION_DIRECTION_TO_PIP, 0);
+                        TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
 
         assertEquals("getAnimator with same type returns same animator",
                 oldAnimator, newAnimator);
@@ -128,6 +132,21 @@
     }
 
     @Test
+    public void pipTransitionAnimator_rotatedEndValue() {
+        final Rect startBounds = new Rect(200, 700, 400, 800);
+        final Rect endBounds = new Rect(0, 0, 500, 1000);
+        final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController
+                .getAnimator(mTaskInfo, mLeash, null, startBounds, endBounds, null,
+                        TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_90);
+        // Apply fraction 1 to compute the end value.
+        animator.applySurfaceControlTransaction(mLeash, new DummySurfaceControlTx(), 1);
+        final Rect rotatedEndBounds = new Rect(endBounds);
+        DisplayLayout.rotateBounds(rotatedEndBounds, endBounds, ROTATION_90);
+
+        assertEquals("Expect 90 degree rotated bounds", rotatedEndBounds, animator.mCurrentValue);
+    }
+
+    @Test
     @SuppressWarnings("unchecked")
     public void pipTransitionAnimator_updateEndValue() {
         final Rect baseValue = new Rect(0, 0, 100, 100);
@@ -136,7 +155,7 @@
         final Rect endValue2 = new Rect(200, 200, 300, 300);
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
-                        TRANSITION_DIRECTION_TO_PIP, 0);
+                        TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
 
         animator.updateEndValue(endValue2);
 
@@ -150,7 +169,7 @@
         final Rect endValue = new Rect(100, 100, 200, 200);
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
-                        TRANSITION_DIRECTION_TO_PIP, 0);
+                        TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
         animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
 
         animator.setPipAnimationCallback(mPipAnimationCallback);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
index 414a0a7..27e5f51 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
@@ -47,6 +47,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.startingsurface.TaskSnapshotWindow;
 
 import org.junit.Test;
@@ -83,7 +84,7 @@
                 createTaskDescription(Color.WHITE, Color.RED, Color.BLUE),
                 0 /* appearance */, windowFlags /* windowFlags */, 0 /* privateWindowFlags */,
                 taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, new InsetsState(),
-                null /* clearWindow */);
+                null /* clearWindow */, new TestShellExecutor());
     }
 
     private TaskSnapshot createTaskSnapshot(int width, int height, Point taskSize,
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ce1d96c..f481228 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -335,7 +335,6 @@
         "jni/YuvToJpegEncoder.cpp",
         "jni/fonts/Font.cpp",
         "jni/fonts/FontFamily.cpp",
-        "jni/fonts/NativeFont.cpp",
         "jni/text/LineBreaker.cpp",
         "jni/text/MeasuredText.cpp",
         "jni/text/TextShaper.cpp",
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index 0fad2d5..e1f5abd7 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -69,7 +69,6 @@
 extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
 extern int register_android_graphics_fonts_Font(JNIEnv* env);
 extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
-extern int register_android_graphics_fonts_NativeFont(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
@@ -136,7 +135,6 @@
     REG_JNI(register_android_graphics_drawable_VectorDrawable),
     REG_JNI(register_android_graphics_fonts_Font),
     REG_JNI(register_android_graphics_fonts_FontFamily),
-    REG_JNI(register_android_graphics_fonts_NativeFont),
     REG_JNI(register_android_graphics_pdf_PdfDocument),
     REG_JNI(register_android_graphics_pdf_PdfEditor),
     REG_JNI(register_android_graphics_pdf_PdfRenderer),
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
index 97c40d6..fa1752c 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -115,6 +115,18 @@
     return reinterpret_cast<jlong>(composeFilter.release());
 }
 
+static jlong createShaderEffect(
+    JNIEnv* env,
+    jobject,
+    jlong shaderHandle
+) {
+    auto* shader = reinterpret_cast<const SkShader*>(shaderHandle);
+    sk_sp<SkImageFilter> shaderFilter = SkImageFilters::Shader(
+          sk_ref_sp(shader), nullptr
+    );
+    return reinterpret_cast<jlong>(shaderFilter.release());
+}
+
 static void RenderEffect_safeUnref(SkImageFilter* filter) {
     SkSafeUnref(filter);
 }
@@ -130,7 +142,8 @@
     {"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect},
     {"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect},
     {"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect},
-    {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect}
+    {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect},
+    {"nativeCreateShaderEffect", "(J)J", (void*)createShaderEffect}
 };
 
 int register_android_graphics_RenderEffect(JNIEnv* env) {
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index b769d40..3392dac 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -34,6 +34,7 @@
 #include <hwui/Typeface.h>
 #include <minikin/FontFamily.h>
 #include <minikin/FontFileParser.h>
+#include <minikin/LocaleList.h>
 #include <ui/FatVector.h>
 
 #include <memory>
@@ -149,12 +150,8 @@
     return reinterpret_cast<jlong>(new FontWrapper(std::move(newFont)));
 }
 
-// Critical Native
-static jlong Font_Builder_getReleaseNativeFont(CRITICAL_JNI_PARAMS) {
-    return reinterpret_cast<jlong>(releaseFont);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
+// Font JNI functions
 
 // Fast Native
 static jfloat Font_getGlyphBounds(JNIEnv* env, jobject, jlong fontHandle, jint glyphId,
@@ -195,51 +192,92 @@
 }
 
 // Critical Native
-static jlong Font_getNativeFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
-    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
-    return reinterpret_cast<jlong>(font->font.get());
+static jlong Font_getMinikinFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    return reinterpret_cast<jlong>(font->font->typeface().get());
 }
 
 // Critical Native
-static jlong Font_GetBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
-    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
-    const void* bufferPtr = font->font->typeface()->GetFontData();
-    return reinterpret_cast<jlong>(bufferPtr);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-struct FontBufferWrapper {
-    FontBufferWrapper(const std::shared_ptr<minikin::MinikinFont>& font) : minikinFont(font) {}
-    // MinikinFont holds a shared pointer of SkTypeface which has reference to font data.
-    std::shared_ptr<minikin::MinikinFont> minikinFont;
-};
-
-static void unrefBuffer(jlong nativePtr) {
-    FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
-    delete wrapper;
-}
-
-// Critical Native
-static jlong FontBufferHelper_refFontBuffer(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
-    const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
-    return reinterpret_cast<jlong>(new FontBufferWrapper(font->typeface()));
+static jlong Font_cloneFont(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    std::shared_ptr<minikin::Font> ref = font->font;
+    return reinterpret_cast<jlong>(new FontWrapper(std::move(ref)));
 }
 
 // Fast Native
-static jobject FontBufferHelper_wrapByteBuffer(JNIEnv* env, jobject, jlong nativePtr) {
-    FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
-    return env->NewDirectByteBuffer(
-        const_cast<void*>(wrapper->minikinFont->GetFontData()),
-        wrapper->minikinFont->GetFontSize());
+static jobject Font_newByteBuffer(JNIEnv* env, jobject, jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+    return env->NewDirectByteBuffer(const_cast<void*>(minikinFont->GetFontData()),
+                                    minikinFont->GetFontSize());
 }
 
 // Critical Native
-static jlong FontBufferHelper_getReleaseFunc(CRITICAL_JNI_PARAMS) {
-    return reinterpret_cast<jlong>(unrefBuffer);
+static jlong Font_getBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    return reinterpret_cast<jlong>(font->font->typeface()->GetFontData());
 }
 
-///////////////////////////////////////////////////////////////////////////////
+// Critical Native
+static jlong Font_getReleaseNativeFontFunc() {
+    return reinterpret_cast<jlong>(releaseFont);
+}
+
+// Fast Native
+static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+    const std::string& path = minikinFont->GetFontPath();
+    if (path.empty()) {
+        return nullptr;
+    }
+    return env->NewStringUTF(path.c_str());
+}
+
+// Fast Native
+static jstring Font_getLocaleList(JNIEnv* env, jobject, jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    uint32_t localeListId = font->font->getLocaleListId();
+    if (localeListId == 0) {
+        return nullptr;
+    }
+    std::string langTags = minikin::getLocaleString(localeListId);
+    if (langTags.empty()) {
+        return nullptr;
+    }
+    return env->NewStringUTF(langTags.c_str());
+}
+
+// Critical Native
+static jint Font_getPackedStyle(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    uint32_t weight = font->font->style().weight();
+    uint32_t isItalic = font->font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 1 : 0;
+    return (isItalic << 16) | weight;
+}
+
+// Critical Native
+static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+    return minikinFont->GetFontIndex();
+}
+
+// Critical Native
+static jint Font_getAxisCount(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+    return minikinFont->GetAxes().size();
+}
+
+// Critical Native
+static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr, jint index) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+    minikin::FontVariation var = minikinFont->GetAxes().at(index);
+    uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
+    return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
+}
 
 // Fast Native
 static jlong FontFileUtil_getFontRevision(JNIEnv* env, jobject, jobject buffer, jint index) {
@@ -314,20 +352,23 @@
         {"nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;Ljava/lang/String;IZI)J",
          (void*)Font_Builder_build},
         {"nClone", "(JJIZI)J", (void*)Font_Builder_clone},
-        {"nGetReleaseNativeFont", "()J", (void*)Font_Builder_getReleaseNativeFont},
 };
 
 static const JNINativeMethod gFontMethods[] = {
-    { "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds },
-    { "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics },
-    { "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr },
-    { "nGetFontBufferAddress", "(J)J", (void*) Font_GetBufferAddress },
-};
-
-static const JNINativeMethod gFontBufferHelperMethods[] = {
-    { "nRefFontBuffer", "(J)J", (void*) FontBufferHelper_refFontBuffer },
-    { "nWrapByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) FontBufferHelper_wrapByteBuffer },
-    { "nGetReleaseFunc", "()J", (void*) FontBufferHelper_getReleaseFunc },
+        {"nGetMinikinFontPtr", "(J)J", (void*)Font_getMinikinFontPtr},
+        {"nCloneFont", "(J)J", (void*)Font_cloneFont},
+        {"nNewByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*)Font_newByteBuffer},
+        {"nGetBufferAddress", "(J)J", (void*)Font_getBufferAddress},
+        {"nGetReleaseNativeFont", "()J", (void*)Font_getReleaseNativeFontFunc},
+        {"nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*)Font_getGlyphBounds},
+        {"nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F",
+         (void*)Font_getFontMetrics},
+        {"nGetFontPath", "(J)Ljava/lang/String;", (void*)Font_getFontPath},
+        {"nGetLocaleList", "(J)Ljava/lang/String;", (void*)Font_getLocaleList},
+        {"nGetPackedStyle", "(J)I", (void*)Font_getPackedStyle},
+        {"nGetIndex", "(J)I", (void*)Font_getIndex},
+        {"nGetAxisCount", "(J)I", (void*)Font_getAxisCount},
+        {"nGetAxisInfo", "(JI)J", (void*)Font_getAxisInfo},
 };
 
 static const JNINativeMethod gFontFileUtilMethods[] = {
@@ -343,8 +384,6 @@
             NELEM(gFontBuilderMethods)) +
             RegisterMethodsOrDie(env, "android/graphics/fonts/Font", gFontMethods,
             NELEM(gFontMethods)) +
-            RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFontBufferHelper",
-            gFontBufferHelperMethods, NELEM(gFontBufferHelperMethods)) +
             RegisterMethodsOrDie(env, "android/graphics/fonts/FontFileUtil", gFontFileUtilMethods,
             NELEM(gFontFileUtilMethods));
 }
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index 8fe6da318..8096479 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -100,17 +100,31 @@
     return static_cast<jint>(family->family->variant());
 }
 
+// CriticalNative
+static jint FontFamily_getFontSize(jlong familyPtr) {
+    FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+    return family->family->getNumFonts();
+}
+
+// CriticalNative
+static jlong FontFamily_getFont(jlong familyPtr, jint index) {
+    FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+    std::shared_ptr<minikin::Font> font = family->family->getFontRef(index);
+    return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gFontFamilyBuilderMethods[] = {
     { "nInitBuilder", "()J", (void*) FontFamily_Builder_initBuilder },
     { "nAddFont", "(JJ)V", (void*) FontFamily_Builder_addFont },
     { "nBuild", "(JLjava/lang/String;IZ)J", (void*) FontFamily_Builder_build },
-
     { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
 };
 
 static const JNINativeMethod gFontFamilyMethods[] = {
+        {"nGetFontSize", "(J)I", (void*)FontFamily_getFontSize},
+        {"nGetFont", "(JI)J", (void*)FontFamily_getFont},
         {"nGetLangTags", "(J)Ljava/lang/String;", (void*)FontFamily_getLangTags},
         {"nGetVariant", "(J)I", (void*)FontFamily_getVariant},
 };
diff --git a/libs/hwui/jni/fonts/NativeFont.cpp b/libs/hwui/jni/fonts/NativeFont.cpp
deleted file mode 100644
index c5c5d46..0000000
--- a/libs/hwui/jni/fonts/NativeFont.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "Minikin"
-
-#include "Font.h"
-#include "SkData.h"
-#include "SkFont.h"
-#include "SkFontMetrics.h"
-#include "SkFontMgr.h"
-#include "SkRefCnt.h"
-#include "SkTypeface.h"
-#include "GraphicsJNI.h"
-#include <nativehelper/ScopedUtfChars.h>
-#include "Utils.h"
-#include "FontUtils.h"
-
-#include <hwui/MinikinSkia.h>
-#include <hwui/Paint.h>
-#include <hwui/Typeface.h>
-#include <minikin/FontFamily.h>
-#include <minikin/LocaleList.h>
-#include <ui/FatVector.h>
-
-#include <memory>
-
-namespace android {
-
-// Critical Native
-static jint NativeFont_getFamilyCount(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle) {
-    Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle);
-    return tf->fFontCollection->getFamilies().size();
-}
-
-// Critical Native
-static jlong NativeFont_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle, jint index) {
-    Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle);
-    return reinterpret_cast<jlong>(tf->fFontCollection->getFamilies()[index].get());
-
-}
-
-// Fast Native
-static jstring NativeFont_getLocaleList(JNIEnv* env, jobject, jlong familyHandle) {
-    minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
-    uint32_t localeListId = family->localeListId();
-    return env->NewStringUTF(minikin::getLocaleString(localeListId).c_str());
-}
-
-// Critical Native
-static jint NativeFont_getFontCount(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle) {
-    minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
-    return family->getNumFonts();
-}
-
-// Critical Native
-static jlong NativeFont_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle, jint index) {
-    minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
-    return reinterpret_cast<jlong>(family->getFont(index));
-}
-
-// Critical Native
-static jlong NativeFont_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
-    const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
-    MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
-
-    uint64_t result = font->style().weight();
-    result |= font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000;
-    result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32);
-    result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48);
-    return result;
-}
-
-// Critical Native
-static jlong NativeFont_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) {
-    const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
-    MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
-    const minikin::FontVariation& var = minikinSkia->GetAxes().at(index);
-    uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
-    return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
-}
-
-// FastNative
-static jstring NativeFont_getFontPath(JNIEnv* env, jobject, jlong fontHandle) {
-    const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
-    MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
-    const std::string& filePath = minikinSkia->getFilePath();
-    if (filePath.empty()) {
-        return nullptr;
-    }
-    return env->NewStringUTF(filePath.c_str());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static const JNINativeMethod gNativeFontMethods[] = {
-    { "nGetFamilyCount", "(J)I", (void*) NativeFont_getFamilyCount },
-    { "nGetFamily", "(JI)J", (void*) NativeFont_getFamily },
-    { "nGetLocaleList", "(J)Ljava/lang/String;", (void*) NativeFont_getLocaleList },
-    { "nGetFontCount", "(J)I", (void*) NativeFont_getFontCount },
-    { "nGetFont", "(JI)J", (void*) NativeFont_getFont },
-    { "nGetFontInfo", "(J)J", (void*) NativeFont_getFontInfo },
-    { "nGetAxisInfo", "(JI)J", (void*) NativeFont_getAxisInfo },
-    { "nGetFontPath", "(J)Ljava/lang/String;", (void*) NativeFont_getFontPath },
-};
-
-int register_android_graphics_fonts_NativeFont(JNIEnv* env) {
-    return RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFont", gNativeFontMethods,
-            NELEM(gNativeFontMethods));
-}
-
-}  // namespace android
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index 9785aa5..a6fb958 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -23,13 +23,14 @@
 #include <set>
 #include <algorithm>
 
-#include "SkPaint.h"
-#include "SkTypeface.h"
 #include <hwui/MinikinSkia.h>
 #include <hwui/MinikinUtils.h>
 #include <hwui/Paint.h>
-#include <minikin/MinikinPaint.h>
 #include <minikin/MinikinFont.h>
+#include <minikin/MinikinPaint.h>
+#include "FontUtils.h"
+#include "SkPaint.h"
+#include "SkTypeface.h"
 
 namespace android {
 
@@ -149,7 +150,8 @@
 // CriticalNative
 static jlong TextShaper_Result_getFont(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
     const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
-    return reinterpret_cast<jlong>(layout->layout.getFont(i));
+    std::shared_ptr<minikin::Font> fontRef = layout->layout.getFontRef(i);
+    return reinterpret_cast<jlong>(new FontWrapper(std::move(fontRef)));
 }
 
 // CriticalNative
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index 06f158f..6a9a98d 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -40,9 +40,9 @@
     return info;
 }
 
-const DisplayConfig& getActiveDisplayConfig() {
-    static DisplayConfig config = [] {
-        DisplayConfig config;
+const ui::DisplayMode& getActiveDisplayMode() {
+    static ui::DisplayMode config = [] {
+        ui::DisplayMode config;
 #if HWUI_NULL_GPU
         config.resolution = ui::Size(1080, 1920);
         config.xDpi = config.yDpi = 320.f;
@@ -51,7 +51,7 @@
         const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken();
         LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__);
 
-        const status_t status = SurfaceComposerClient::getActiveDisplayConfig(token, &config);
+        const status_t status = SurfaceComposerClient::getActiveDisplayMode(token, &config);
         LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get active display config", __FUNCTION__);
 #endif
         return config;
diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h
index a012ecb..7d2f6d8 100644
--- a/libs/hwui/tests/common/TestContext.h
+++ b/libs/hwui/tests/common/TestContext.h
@@ -23,8 +23,8 @@
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <gui/SurfaceControl.h>
-#include <ui/DisplayConfig.h>
 #include <ui/DisplayInfo.h>
+#include <ui/DisplayMode.h>
 #include <utils/Looper.h>
 
 #include <atomic>
@@ -37,10 +37,10 @@
 namespace test {
 
 const DisplayInfo& getDisplayInfo();
-const DisplayConfig& getActiveDisplayConfig();
+const ui::DisplayMode& getActiveDisplayMode();
 
 inline const ui::Size& getActiveDisplayResolution() {
-    return getActiveDisplayConfig().resolution;
+    return getActiveDisplayMode().resolution;
 }
 
 class TestContext {
diff --git a/libs/tracingproxy/Android.bp b/libs/tracingproxy/Android.bp
new file mode 100644
index 0000000..67f407f
--- /dev/null
+++ b/libs/tracingproxy/Android.bp
@@ -0,0 +1,42 @@
+// Copyright (C) 2020 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.
+
+// Provides C++ wrappers for system services.
+
+cc_library_shared {
+    name: "libtracingproxy",
+
+    aidl: {
+        export_aidl_headers: true,
+        include_dirs: [
+            "frameworks/base/core/java",
+        ],
+    },
+
+    srcs: [
+        ":ITracingServiceProxy.aidl",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 65721cc..adf58da 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -36,6 +36,7 @@
 import android.location.Location;
 import android.location.LocationRequest;
 import android.location.LocationTime;
+import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
 import android.os.Bundle;
 import android.os.ICancellationSignal;
@@ -91,6 +92,9 @@
     void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, @nullable String attributionTag);
     void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
 
+    void addProviderRequestListener(in IProviderRequestListener listener);
+    void removeProviderRequestListener(in IProviderRequestListener listener);
+
     int getGnssBatchSize();
     void startGnssBatch(long periodNanos, in ILocationListener listener, String packageName, @nullable String attributionTag, @nullable String listenerId);
     void flushGnssBatch();
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d569482..088b789 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -49,7 +49,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
+import android.location.provider.ProviderRequest.Listener;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -436,6 +439,9 @@
                 new GnssNavigationTransportManager();
     }
 
+    private static final ProviderRequestTransportManager sProviderRequestListeners =
+            new ProviderRequestTransportManager();
+
     private final Context mContext;
     private final ILocationManager mService;
 
@@ -2772,6 +2778,37 @@
     }
 
     /**
+     * Registers a {@link ProviderRequest.Listener} to all providers.
+     *
+     * @param executor the executor that the callback runs on
+     * @param listener the listener to register
+     * @return {@code true} always
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+    public boolean registerProviderRequestListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Listener listener) {
+        sProviderRequestListeners.addListener(listener,
+                new ProviderRequestTransport(executor, listener));
+        return true;
+    }
+
+    /**
+     * Unregisters a {@link ProviderRequest.Listener}.
+     *
+     * @param listener the listener to remove.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+    public void unregisterProviderRequestListener(
+            @NonNull Listener listener) {
+        sProviderRequestListeners.removeListener(listener);
+    }
+
+    /**
      * Returns the batch size (in number of Location objects) that are supported by the batching
      * interface.
      *
@@ -2960,6 +2997,22 @@
         }
     }
 
+    private static class ProviderRequestTransportManager extends
+            ListenerTransportManager<ProviderRequestTransport> {
+
+        @Override
+        protected void registerTransport(ProviderRequestTransport transport)
+                throws RemoteException {
+            getService().addProviderRequestListener(transport);
+        }
+
+        @Override
+        protected void unregisterTransport(ProviderRequestTransport transport)
+                throws RemoteException {
+            getService().removeProviderRequestListener(transport);
+        }
+    }
+
     private static class GetCurrentLocationTransport extends ILocationCallback.Stub implements
             ListenerExecutor, CancellationSignal.OnCancelListener {
 
@@ -3359,6 +3412,36 @@
         }
     }
 
+    private static class ProviderRequestTransport extends IProviderRequestListener.Stub
+            implements ListenerTransport<ProviderRequest.Listener> {
+
+        private final Executor mExecutor;
+
+        private volatile @Nullable ProviderRequest.Listener mListener;
+
+        ProviderRequestTransport(Executor executor, ProviderRequest.Listener listener) {
+            Preconditions.checkArgument(executor != null, "invalid null executor");
+            Preconditions.checkArgument(listener != null, "invalid null callback");
+            mExecutor = executor;
+            mListener = listener;
+        }
+
+        @Override
+        public void unregister() {
+            mListener = null;
+        }
+
+        @Override
+        public @Nullable ProviderRequest.Listener getListener() {
+            return mListener;
+        }
+
+        @Override
+        public void onProviderRequestChanged(String provider, ProviderRequest request) {
+            execute(mExecutor, listener -> listener.onProviderRequestChanged(provider, request));
+        }
+    }
+
     private static class BatchedLocationCallbackWrapper implements LocationListener {
 
         private final BatchedLocationCallback mCallback;
diff --git a/location/java/android/location/provider/IProviderRequestListener.aidl b/location/java/android/location/provider/IProviderRequestListener.aidl
new file mode 100644
index 0000000..89d454a
--- /dev/null
+++ b/location/java/android/location/provider/IProviderRequestListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.location.provider.ProviderRequest;
+
+/**
+ * {@hide}
+ */
+oneway interface IProviderRequestListener {
+    void onProviderRequestChanged(String provider, in ProviderRequest request);
+}
diff --git a/location/java/android/location/provider/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java
index e543b04..b6ec323 100644
--- a/location/java/android/location/provider/ProviderRequest.java
+++ b/location/java/android/location/provider/ProviderRequest.java
@@ -53,6 +53,17 @@
     private final boolean mLocationSettingsIgnored;
     private final WorkSource mWorkSource;
 
+    /**
+     * Listener to be invoked when a new request is set to the provider.
+     */
+    public interface Listener {
+
+        /**
+         * Invoked when a new request is set.
+         */
+        void onProviderRequestChanged(@NonNull String provider, @NonNull ProviderRequest request);
+    }
+
     private ProviderRequest(
             long intervalMillis,
             @Quality int quality,
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 812dac9..27f72687 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -20,6 +20,7 @@
 import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -195,6 +196,8 @@
 
     private int mDeviceId;
 
+    private int mSessionId;
+
     /**
      * Never use without initializing parameters afterwards
      */
@@ -207,7 +210,10 @@
      * @hide
      */
     public AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid) {
-        if (DEBUG) { Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer); }
+        if (DEBUG) {
+            Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer
+                    + " sessionId=" + pic.mSessionId);
+        }
         mPlayerIId = piid;
         mPlayerType = pic.mPlayerType;
         mClientUid = uid;
@@ -220,6 +226,7 @@
         } else {
             mIPlayerShell = null;
         }
+        mSessionId = pic.mSessionId;
     }
 
     /**
@@ -259,6 +266,7 @@
         anonymCopy.mClientUid = PLAYER_UPID_INVALID;
         anonymCopy.mClientPid = PLAYER_UPID_INVALID;
         anonymCopy.mIPlayerShell = null;
+        anonymCopy.mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
         return anonymCopy;
     }
 
@@ -303,6 +311,17 @@
 
     /**
      * @hide
+     * Return the audio session ID associated with this player.
+     * See {@link AudioManager#generateAudioSessionId()}.
+     * @return an audio session ID
+     */
+    @SystemApi
+    public @IntRange(from = 0) int getSessionId() {
+        return mSessionId;
+    }
+
+    /**
+     * @hide
      * Return the type of player linked to this configuration.
      * <br>Note that player types not exposed in the system API will be represented as
      * {@link #PLAYER_TYPE_UNKNOWN}.
@@ -381,6 +400,17 @@
 
     /**
      * @hide
+     * Handle a change of audio session Id
+     * @param sessionId the audio session ID
+     */
+    public boolean handleSessionIdEvent(int sessionId) {
+        final boolean changed = sessionId != mSessionId;
+        mSessionId = sessionId;
+        return changed;
+    }
+
+    /**
+     * @hide
      * Handle a player state change
      * @param event
      * @param deviceId active device id or {@Code PLAYER_DEVICEID_INVALID}
@@ -476,7 +506,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid);
+        return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid,
+                mSessionId);
     }
 
     @Override
@@ -498,6 +529,7 @@
             ips = mIPlayerShell;
         }
         dest.writeStrongInterface(ips == null ? null : ips.getIPlayer());
+        dest.writeInt(mSessionId);
     }
 
     private AudioPlaybackConfiguration(Parcel in) {
@@ -510,6 +542,7 @@
         mPlayerAttr = AudioAttributes.CREATOR.createFromParcel(in);
         final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder());
         mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p);
+        mSessionId = in.readInt();
     }
 
     @Override
@@ -523,7 +556,8 @@
                 && (mDeviceId == that.mDeviceId)
                 && (mPlayerType == that.mPlayerType)
                 && (mClientUid == that.mClientUid)
-                && (mClientPid == that.mClientPid));
+                && (mClientPid == that.mClientPid))
+                && (mSessionId == that.mSessionId);
     }
 
     @Override
@@ -533,7 +567,8 @@
                 + " type:" + toLogFriendlyPlayerType(mPlayerType)
                 + " u/pid:" + mClientUid + "/" + mClientPid
                 + " state:" + toLogFriendlyPlayerState(mPlayerState)
-                + " attr:" + mPlayerAttr;
+                + " attr:" + mPlayerAttr
+                + " sessionId:" + mSessionId;
     }
 
     //=====================================================================
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 175d36f..e056d43 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -836,7 +836,7 @@
             mState = STATE_INITIALIZED;
         }
 
-        baseRegisterPlayer();
+        baseRegisterPlayer(mSessionId);
     }
 
     /**
@@ -866,7 +866,7 @@
 
         // other initialization...
         if (nativeTrackInJavaObj != 0) {
-            baseRegisterPlayer();
+            baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
             deferred_connect(nativeTrackInJavaObj);
         } else {
             mState = STATE_UNINITIALIZED;
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index 06bf5f7..9c6b276 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -119,9 +119,14 @@
      */
     public static final int QUALITY_2K = 12;
 
+    /**
+     * Quality level corresponding to 8K UHD (7680 x 4320) resolution
+     */
+    public static final int QUALITY_8KUHD = 13;
+
     // Start and end of quality list
     private static final int QUALITY_LIST_START = QUALITY_LOW;
-    private static final int QUALITY_LIST_END = QUALITY_2K;
+    private static final int QUALITY_LIST_END = QUALITY_8KUHD;
 
     /**
      * Time lapse quality level corresponding to the lowest available resolution.
@@ -188,10 +193,14 @@
      */
     public static final int QUALITY_TIME_LAPSE_2K = 1012;
 
+    /**
+     * Time lapse quality level corresponding to the 8K UHD (7680 x 4320) resolution.
+     */
+    public static final int QUALITY_TIME_LAPSE_8KUHD = 1013;
 
     // Start and end of timelapse quality list
     private static final int QUALITY_TIME_LAPSE_LIST_START = QUALITY_TIME_LAPSE_LOW;
-    private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_2K;
+    private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_8KUHD;
 
     /**
      * High speed ( >= 100fps) quality level corresponding to the lowest available resolution.
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
index bbf632a..e339ae8 100644
--- a/media/java/android/media/HwAudioSource.java
+++ b/media/java/android/media/HwAudioSource.java
@@ -54,7 +54,7 @@
         Preconditions.checkArgument(device.isSource(), "Requires a source device");
         mAudioDeviceInfo = device;
         mAudioAttributes = attributes;
-        baseRegisterPlayer();
+        baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
     }
 
     /**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index dd42aab..71ee57e 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -73,6 +73,8 @@
 
     oneway void releaseRecorder(in int riid);
 
+    oneway void playerSessionId(in int piid, in int sessionId);
+
     // Java-only methods below.
 
     oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index dd0bc61..ca0d29f 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -663,6 +663,10 @@
      * result in an exception.</p>
      */
     public MediaPlayer() {
+        this(AudioSystem.AUDIO_SESSION_ALLOCATE);
+    }
+
+    private MediaPlayer(int sessionId) {
         super(new AudioAttributes.Builder().build(),
                 AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);
 
@@ -684,7 +688,7 @@
         native_setup(new WeakReference<MediaPlayer>(this),
                 getCurrentOpPackageName());
 
-        baseRegisterPlayer();
+        baseRegisterPlayer(sessionId);
     }
 
     /*
@@ -913,7 +917,7 @@
             AudioAttributes audioAttributes, int audioSessionId) {
 
         try {
-            MediaPlayer mp = new MediaPlayer();
+            MediaPlayer mp = new MediaPlayer(audioSessionId);
             final AudioAttributes aa = audioAttributes != null ? audioAttributes :
                 new AudioAttributes.Builder().build();
             mp.setAudioAttributes(aa);
@@ -978,7 +982,7 @@
             AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
             if (afd == null) return null;
 
-            MediaPlayer mp = new MediaPlayer();
+            MediaPlayer mp = new MediaPlayer(audioSessionId);
 
             final AudioAttributes aa = audioAttributes != null ? audioAttributes :
                 new AudioAttributes.Builder().build();
@@ -2365,7 +2369,13 @@
      * This method must be called before one of the overloaded <code> setDataSource </code> methods.
      * @throws IllegalStateException if it is called in an invalid state
      */
-    public native void setAudioSessionId(int sessionId)  throws IllegalArgumentException, IllegalStateException;
+    public void setAudioSessionId(int sessionId)
+            throws IllegalArgumentException, IllegalStateException {
+        native_setAudioSessionId(sessionId);
+        baseUpdateSessionId(sessionId);
+    }
+
+    private native void native_setAudioSessionId(int sessionId);
 
     /**
      * Returns the audio session ID.
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 58ae279..4407efa 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -97,6 +97,7 @@
      * Constructor. Must be given audio attributes, as they are required for AppOps.
      * @param attr non-null audio attributes
      * @param class non-null class of the implementation of this abstract class
+     * @param sessionId the audio session Id
      */
     PlayerBase(@NonNull AudioAttributes attr, int implType) {
         if (attr == null) {
@@ -110,7 +111,7 @@
     /**
      * Call from derived class when instantiation / initialization is successful
      */
-    protected void baseRegisterPlayer() {
+    protected void baseRegisterPlayer(int sessionId) {
         if (!USE_AUDIOFLINGER_MUTING_FOR_OP) {
             IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
             mAppOps = IAppOpsService.Stub.asInterface(b);
@@ -128,7 +129,8 @@
         }
         try {
             mPlayerIId = getService().trackPlayer(
-                    new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));
+                    new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this),
+                            sessionId));
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
         }
@@ -145,7 +147,7 @@
         try {
             getService().playerAttributes(mPlayerIId, attr);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e);
+            Log.e(TAG, "Error talking to audio service, audio attributes will not be updated", e);
         }
         synchronized (mLock) {
             boolean attributesChanged = (mAttributes != attr);
@@ -154,6 +156,18 @@
         }
     }
 
+    /**
+     * To be called whenever the session ID of the player changes
+     * @param sessionId, the new session Id
+     */
+    void baseUpdateSessionId(int sessionId) {
+        try {
+            getService().playerSessionId(mPlayerIId, sessionId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error talking to audio service, the session ID will not be updated", e);
+        }
+    }
+
     void baseUpdateDeviceId(@Nullable AudioDeviceInfo deviceInfo) {
         int deviceId = 0;
         if (deviceInfo != null) {
@@ -566,16 +580,19 @@
         public static final int AUDIO_ATTRIBUTES_DEFINED = 1;
         public final AudioAttributes mAttributes;
         public final IPlayer mIPlayer;
+        public final int mSessionId;
 
-        PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer) {
+        PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer,
+                     int sessionId) {
             mPlayerType = type;
             mAttributes = attr;
             mIPlayer = iplayer;
+            mSessionId = sessionId;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mPlayerType);
+            return Objects.hash(mPlayerType, mSessionId);
         }
 
         @Override
@@ -588,6 +605,7 @@
             dest.writeInt(mPlayerType);
             mAttributes.writeToParcel(dest, 0);
             dest.writeStrongBinder(mIPlayer == null ? null : mIPlayer.asBinder());
+            dest.writeInt(mSessionId);
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<PlayerIdCard> CREATOR
@@ -611,6 +629,7 @@
             // IPlayer can be null if unmarshalling a Parcel coming from who knows where
             final IBinder b = in.readStrongBinder();
             mIPlayer = (b == null ? null : IPlayer.Stub.asInterface(b));
+            mSessionId = in.readInt();
         }
 
         @Override
@@ -621,7 +640,8 @@
             PlayerIdCard that = (PlayerIdCard) o;
 
             // FIXME change to the binder player interface once supported as a member
-            return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes));
+            return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes)
+                    && (mSessionId == that.mSessionId));
         }
     }
 
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 797caf3..32413dc 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -155,7 +155,8 @@
         }
         mAttributes = attributes;
 
-        baseRegisterPlayer();
+        // FIXME: b/174876164 implement session id for soundpool
+        baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
     }
 
     /**
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 31fb8d0..9bf126b 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -35,7 +35,7 @@
     ISessionController getController();
     void setFlags(int flags);
     void setActive(boolean active);
-    void setMediaButtonReceiver(in PendingIntent mbr);
+    void setMediaButtonReceiver(in PendingIntent mbr, String sessionPackageName);
     void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver);
     void setLaunchPendingIntent(in PendingIntent pi);
     void destroySession();
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 24118b0..20fa53d 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -286,7 +286,7 @@
     @Deprecated
     public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
         try {
-            mBinder.setMediaButtonReceiver(mbr);
+            mBinder.setMediaButtonReceiver(mbr, mContext.getPackageName());
         } catch (RemoteException e) {
             Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
         }
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index bd8d2e9..98ac5b9 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1409,7 +1409,7 @@
     {"native_setup",        "(Ljava/lang/Object;Ljava/lang/String;)V",(void *)android_media_MediaPlayer_native_setup},
     {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},
     {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer_get_audio_session_id},
-    {"setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer_set_audio_session_id},
+    {"native_setAudioSessionId",   "(I)V",                      (void *)android_media_MediaPlayer_set_audio_session_id},
     {"_setAuxEffectSendLevel", "(F)V",                          (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
     {"attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer_attachAuxEffect},
     {"native_pullBatteryData", "(Landroid/os/Parcel;)I",        (void *)android_media_MediaPlayer_pullBatteryData},
diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h
index 31eb35a..c38a8fa 100644
--- a/media/jni/tuner/DemuxClient.h
+++ b/media/jni/tuner/DemuxClient.h
@@ -100,6 +100,11 @@
      */
     Result close();
 
+    /**
+     * Get the Aidl demux to set as source.
+     */
+    shared_ptr<ITunerDemux> getAidlDemux() { return mTunerDemux; }
+
     void setId(int id) { mId = id; }
     int getId() { return mId; }
 
diff --git a/media/jni/tuner/DescramblerClient.cpp b/media/jni/tuner/DescramblerClient.cpp
index 979beea..c9bacda 100644
--- a/media/jni/tuner/DescramblerClient.cpp
+++ b/media/jni/tuner/DescramblerClient.cpp
@@ -27,13 +27,12 @@
 
 /////////////// DescramblerClient ///////////////////////
 
-// TODO: pending aidl interface
-DescramblerClient::DescramblerClient() {
-    //mTunerDescrambler = tunerDescrambler;
+DescramblerClient::DescramblerClient(shared_ptr<ITunerDescrambler> tunerDescrambler) {
+    mTunerDescrambler = tunerDescrambler;
 }
 
 DescramblerClient::~DescramblerClient() {
-    //mTunerDescrambler = NULL;
+    mTunerDescrambler = NULL;
     mDescrambler = NULL;
 }
 
@@ -47,7 +46,10 @@
         return Result::INVALID_ARGUMENT;
     }
 
-    // TODO: pending aidl interface
+    if (mTunerDescrambler != NULL) {
+        Status s = mTunerDescrambler->setDemuxSource(demuxClient->getAidlDemux());
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mDescrambler != NULL) {
         return mDescrambler->setDemuxSource(demuxClient->getId());
@@ -57,7 +59,10 @@
 }
 
 Result DescramblerClient::setKeyToken(vector<uint8_t> keyToken) {
-    // TODO: pending aidl interface
+    if (mTunerDescrambler != NULL) {
+        Status s = mTunerDescrambler->setKeyToken(keyToken);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mDescrambler != NULL) {
         return mDescrambler->setKeyToken(keyToken);
@@ -67,7 +72,11 @@
 }
 
 Result DescramblerClient::addPid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) {
-    // TODO: pending aidl interface
+    if (mTunerDescrambler != NULL) {
+        Status s = mTunerDescrambler->addPid(
+                getAidlDemuxPid(pid), optionalSourceFilter->getAidlFilter());
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mDescrambler != NULL) {
         return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter());
@@ -76,16 +85,24 @@
     return Result::INVALID_STATE;}
 
 Result DescramblerClient::removePid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) {
-    // TODO: pending aidl interface
-
-    if (mDescrambler != NULL) {
-        return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter());
+    if (mTunerDescrambler != NULL) {
+        Status s = mTunerDescrambler->removePid(
+                getAidlDemuxPid(pid), optionalSourceFilter->getAidlFilter());
+        return ClientHelper::getServiceSpecificErrorCode(s);
     }
 
-    return Result::INVALID_STATE;}
+    if (mDescrambler != NULL) {
+        return mDescrambler->removePid(pid, optionalSourceFilter->getHalFilter());
+    }
+
+    return Result::INVALID_STATE;
+}
 
 Result DescramblerClient::close() {
-    // TODO: pending aidl interface
+    if (mTunerDescrambler != NULL) {
+        Status s = mTunerDescrambler->close();
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mDescrambler != NULL) {
         return mDescrambler->close();
@@ -95,4 +112,16 @@
 
 /////////////// DescramblerClient Helper Methods ///////////////////////
 
+TunerDemuxPid DescramblerClient::getAidlDemuxPid(DemuxPid& pid) {
+    TunerDemuxPid aidlPid;
+    switch (pid.getDiscriminator()) {
+        case DemuxPid::hidl_discriminator::tPid:
+            aidlPid.set<TunerDemuxPid::tPid>((int)pid.tPid());
+            break;
+        case DemuxPid::hidl_discriminator::mmtpPid:
+            aidlPid.set<TunerDemuxPid::mmtpPid>((int)pid.mmtpPid());
+            break;
+    }
+    return aidlPid;
+}
 }  // namespace android
diff --git a/media/jni/tuner/DescramblerClient.h b/media/jni/tuner/DescramblerClient.h
index 8af6883..a8fa1e2 100644
--- a/media/jni/tuner/DescramblerClient.h
+++ b/media/jni/tuner/DescramblerClient.h
@@ -17,14 +17,15 @@
 #ifndef _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_
 #define _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_
 
-//#include <aidl/android/media/tv/tuner/ITunerDescrambler.h>
+#include <aidl/android/media/tv/tuner/ITunerDescrambler.h>
 #include <android/hardware/tv/tuner/1.0/IDescrambler.h>
 #include <android/hardware/tv/tuner/1.1/types.h>
 
 #include "DemuxClient.h"
 #include "FilterClient.h"
 
-//using ::aidl::android::media::tv::tuner::ITunerDescrambler;
+using ::aidl::android::media::tv::tuner::ITunerDescrambler;
+using ::aidl::android::media::tv::tuner::TunerDemuxPid;
 
 using ::android::hardware::tv::tuner::V1_0::IDescrambler;
 using ::android::hardware::tv::tuner::V1_0::Result;
@@ -37,8 +38,7 @@
 struct DescramblerClient : public RefBase {
 
 public:
-    // TODO: pending hidl interface
-    DescramblerClient();
+    DescramblerClient(shared_ptr<ITunerDescrambler> tunerDescrambler);
     ~DescramblerClient();
 
     // TODO: remove after migration to Tuner Service is done.
@@ -70,12 +70,13 @@
     Result close();
 
 private:
+    TunerDemuxPid getAidlDemuxPid(DemuxPid& pid);
+
     /**
      * An AIDL Tuner Descrambler Singleton assigned at the first time the Tuner Client
      * opens a descrambler. Default null when descrambler is not opened.
      */
-    // TODO: pending on aidl interface
-    //shared_ptr<ITunerDescrambler> mTunerDescrambler;
+    shared_ptr<ITunerDescrambler> mTunerDescrambler;
 
     /**
      * A Descrambler HAL interface that is ready before migrating to the TunerDescrambler.
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index f454907..3a00133 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -91,8 +91,8 @@
     if (mTunerFrontend != NULL) {
         mAidlCallback = ::ndk::SharedRefBase::make<TunerFrontendCallback>(frontendClientCallback);
         mAidlCallback->setFrontendType(mType);
-        mTunerFrontend->setCallback(mAidlCallback);
-        return Result::SUCCESS;
+        Status s = mTunerFrontend->setCallback(mAidlCallback);
+        return ClientHelper::getServiceSpecificErrorCode(s);
     }
 
     mHidlCallback = new HidlFrontendCallback(frontendClientCallback);
@@ -243,9 +243,8 @@
 
 Result FrontendClient::setLnb(sp<LnbClient> lnbClient) {
     if (mTunerFrontend != NULL) {
-        // TODO: handle error message.
-        /*mTunerFrontend->setLnb(lnbClient->getAidlLnb());
-        return Result::SUCCESS;*/
+        Status s = mTunerFrontend->setLnb(lnbClient->getAidlLnb());
+        return ClientHelper::getServiceSpecificErrorCode(s);
     }
 
     if (mFrontend != NULL) {
@@ -258,9 +257,8 @@
 
 Result FrontendClient::setLna(bool bEnable) {
     if (mTunerFrontend != NULL) {
-        // TODO: handle error message.
-        /*mTunerFrontend->setLna(bEnable);
-        return Result::SUCCESS;*/
+        Status s = mTunerFrontend->setLna(bEnable);
+        return ClientHelper::getServiceSpecificErrorCode(s);
     }
 
     if (mFrontend != NULL) {
@@ -275,9 +273,11 @@
     int ltsId = (int)Constant::INVALID_LTS_ID;
 
     if (mTunerFrontend != NULL) {
-        // TODO: handle error message.
-        /*mTunerFrontend->linkCiCamToFrontend(ciCamId, ltsId);
-        return ltsId;*/
+        Status s = mTunerFrontend->linkCiCamToFrontend(ciCamId, &ltsId);
+        if (ClientHelper::getServiceSpecificErrorCode(s) == Result::SUCCESS) {
+            return ltsId;
+        }
+        return (int)Constant::INVALID_LTS_ID;
     }
 
     if (mFrontend_1_1 != NULL) {
@@ -297,9 +297,8 @@
 
 Result FrontendClient::unlinkCiCamToFrontend(int ciCamId) {
     if (mTunerFrontend != NULL) {
-        // TODO: handle error message.
-        /*mTunerFrontend->unlinkCiCamToFrontend(ciCamId);
-        return Result::SUCCESS;*/
+        Status s = mTunerFrontend->unlinkCiCamToFrontend(ciCamId);
+        return ClientHelper::getServiceSpecificErrorCode(s);
     }
 
     if (mFrontend_1_1 != NULL) {
diff --git a/media/jni/tuner/LnbClient.h b/media/jni/tuner/LnbClient.h
index e7869e8..465dc23 100644
--- a/media/jni/tuner/LnbClient.h
+++ b/media/jni/tuner/LnbClient.h
@@ -108,7 +108,7 @@
      */
     Result close();
 
-    //shared_ptr<ITunerLnb> getAidlLnb() { return mTunerLnb; }
+    shared_ptr<ITunerLnb> getAidlLnb() { return mTunerLnb; }
     void setId(LnbId id) { mId = id; }
     LnbId getId() { return mId; }
 
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index a604490d..7f954b5 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -22,7 +22,10 @@
 
 #include "TunerClient.h"
 
+using ::aidl::android::media::tv::tuner::TunerFrontendCapabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendDtmbCapabilities;
 using ::android::hardware::tv::tuner::V1_0::FrontendId;
+using ::android::hardware::tv::tuner::V1_0::FrontendStatusType;
 using ::android::hardware::tv::tuner::V1_0::FrontendType;
 
 namespace android {
@@ -136,12 +139,11 @@
 shared_ptr<FrontendInfo> TunerClient::getFrontendInfo(int id) {
     if (mTunerService != NULL) {
         TunerFrontendInfo aidlFrontendInfo;
-        // TODO: handle error code
         Status s = mTunerService->getFrontendInfo(id, &aidlFrontendInfo);
         if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
             return NULL;
         }
-        return make_shared<FrontendInfo>(FrontendInfoAidlToHidl(aidlFrontendInfo));
+        return make_shared<FrontendInfo>(frontendInfoAidlToHidl(aidlFrontendInfo));
     }
 
     if (mTuner != NULL) {
@@ -157,7 +159,22 @@
 }
 
 shared_ptr<FrontendDtmbCapabilities> TunerClient::getFrontendDtmbCapabilities(int id) {
-    // pending aidl interface
+    if (mTunerService != NULL) {
+        TunerFrontendDtmbCapabilities dtmbCaps;
+        Status s = mTunerService->getFrontendDtmbCapabilities(id, &dtmbCaps);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return NULL;
+        }
+        FrontendDtmbCapabilities hidlCaps{
+            .transmissionModeCap = static_cast<uint32_t>(dtmbCaps.transmissionModeCap),
+            .bandwidthCap = static_cast<uint32_t>(dtmbCaps.bandwidthCap),
+            .modulationCap = static_cast<uint32_t>(dtmbCaps.modulationCap),
+            .codeRateCap = static_cast<uint32_t>(dtmbCaps.codeRateCap),
+            .guardIntervalCap = static_cast<uint32_t>(dtmbCaps.guardIntervalCap),
+            .interleaveModeCap = static_cast<uint32_t>(dtmbCaps.interleaveModeCap),
+        };
+        return make_shared<FrontendDtmbCapabilities>(hidlCaps);
+    }
 
     if (mTuner_1_1 != NULL) {
         Result result;
@@ -224,17 +241,18 @@
     return NULL;
 }
 
-sp<DescramblerClient> TunerClient::openDescrambler(int /*descramblerHandle*/) {
+sp<DescramblerClient> TunerClient::openDescrambler(int descramblerHandle) {
     if (mTunerService != NULL) {
-        // TODO: handle error code
-        /*shared_ptr<ITunerDescrambler> tunerDescrambler;
-        mTunerService->openDescrambler(demuxHandle, &tunerDescrambler);
-        return new DescramblerClient(tunerDescrambler);*/
+        shared_ptr<ITunerDescrambler> tunerDescrambler;
+        Status s = mTunerService->openDescrambler(descramblerHandle, &tunerDescrambler);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return NULL;
+        }
+        return new DescramblerClient(tunerDescrambler);
     }
 
     if (mTuner != NULL) {
-        // TODO: pending aidl interface
-        sp<DescramblerClient> descramblerClient = new DescramblerClient();
+        sp<DescramblerClient> descramblerClient = new DescramblerClient(NULL);
         sp<IDescrambler> hidlDescrambler = openHidlDescrambler();
         if (hidlDescrambler != NULL) {
             descramblerClient->setHidlDescrambler(hidlDescrambler);
@@ -486,7 +504,7 @@
     return caps;
 }
 
-FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
+FrontendInfo TunerClient::frontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
     FrontendInfo hidlFrontendInfo {
         .type = static_cast<FrontendType>(aidlFrontendInfo.type),
         .minFrequency = static_cast<uint32_t>(aidlFrontendInfo.minFrequency),
@@ -496,8 +514,102 @@
         .acquireRange = static_cast<uint32_t>(aidlFrontendInfo.acquireRange),
         .exclusiveGroupId = static_cast<uint32_t>(aidlFrontendInfo.exclusiveGroupId),
     };
-    // TODO: handle Frontend caps
 
+    int size = aidlFrontendInfo.statusCaps.size();
+    hidlFrontendInfo.statusCaps.resize(size);
+    for (int i = 0; i < size; i++) {
+        hidlFrontendInfo.statusCaps[i] =
+                static_cast<FrontendStatusType>(aidlFrontendInfo.statusCaps[i]);
+    }
+
+    switch (aidlFrontendInfo.caps.getTag()) {
+        case TunerFrontendCapabilities::analogCaps: {
+            auto analog = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::analogCaps>();
+            hidlFrontendInfo.frontendCaps.analogCaps({
+                .typeCap = static_cast<uint32_t>(analog.typeCap),
+                .sifStandardCap = static_cast<uint32_t>(analog.sifStandardCap),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::atscCaps: {
+            auto atsc = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::atscCaps>();
+            hidlFrontendInfo.frontendCaps.atscCaps({
+                .modulationCap = static_cast<uint32_t>(atsc.modulationCap),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::atsc3Caps: {
+            auto atsc3 = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::atsc3Caps>();
+            hidlFrontendInfo.frontendCaps.atsc3Caps({
+                .bandwidthCap = static_cast<uint32_t>(atsc3.bandwidthCap),
+                .modulationCap = static_cast<uint32_t>(atsc3.modulationCap),
+                .timeInterleaveModeCap = static_cast<uint32_t>(atsc3.timeInterleaveModeCap),
+                .codeRateCap = static_cast<uint32_t>(atsc3.codeRateCap),
+                .fecCap = static_cast<uint32_t>(atsc3.fecCap),
+                .demodOutputFormatCap = static_cast<uint8_t>(atsc3.demodOutputFormatCap),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::cableCaps: {
+            auto cable = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::cableCaps>();
+            hidlFrontendInfo.frontendCaps.dvbcCaps({
+                .modulationCap = static_cast<uint32_t>(cable.modulationCap),
+                .fecCap = static_cast<uint64_t>(cable.codeRateCap),
+                .annexCap = static_cast<uint8_t>(cable.annexCap),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::dvbsCaps: {
+            auto dvbs = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::dvbsCaps>();
+            hidlFrontendInfo.frontendCaps.dvbsCaps({
+                .modulationCap = static_cast<int32_t>(dvbs.modulationCap),
+                .innerfecCap = static_cast<uint64_t>(dvbs.codeRateCap),
+                .standard = static_cast<uint8_t>(dvbs.standard),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::dvbtCaps: {
+            auto dvbt = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::dvbtCaps>();
+            hidlFrontendInfo.frontendCaps.dvbtCaps({
+                .transmissionModeCap = static_cast<uint32_t>(dvbt.transmissionModeCap),
+                .bandwidthCap = static_cast<uint32_t>(dvbt.bandwidthCap),
+                .constellationCap = static_cast<uint32_t>(dvbt.constellationCap),
+                .coderateCap = static_cast<uint32_t>(dvbt.codeRateCap),
+                .hierarchyCap = static_cast<uint32_t>(dvbt.hierarchyCap),
+                .guardIntervalCap = static_cast<uint32_t>(dvbt.guardIntervalCap),
+                .isT2Supported = dvbt.isT2Supported,
+                .isMisoSupported = dvbt.isMisoSupported,
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::isdbsCaps: {
+            auto isdbs = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbsCaps>();
+            hidlFrontendInfo.frontendCaps.isdbsCaps({
+                .modulationCap = static_cast<uint32_t>(isdbs.modulationCap),
+                .coderateCap = static_cast<uint32_t>(isdbs.codeRateCap),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::isdbs3Caps: {
+            auto isdbs3 = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbs3Caps>();
+            hidlFrontendInfo.frontendCaps.isdbs3Caps({
+                .modulationCap = static_cast<uint32_t>(isdbs3.modulationCap),
+                .coderateCap = static_cast<uint32_t>(isdbs3.codeRateCap),
+            });
+            break;
+        }
+        case TunerFrontendCapabilities::isdbtCaps: {
+            auto isdbt = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbtCaps>();
+            hidlFrontendInfo.frontendCaps.isdbtCaps({
+                .modeCap = static_cast<uint32_t>(isdbt.modeCap),
+                .bandwidthCap = static_cast<uint32_t>(isdbt.bandwidthCap),
+                .modulationCap = static_cast<uint32_t>(isdbt.modulationCap),
+                .coderateCap = static_cast<uint32_t>(isdbt.codeRateCap),
+                .guardIntervalCap = static_cast<uint32_t>(isdbt.guardIntervalCap),
+            });
+            break;
+        }
+    }
     return hidlFrontendInfo;
 }
 
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index acd018e..744bf20 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -147,7 +147,7 @@
     sp<IDescrambler> openHidlDescrambler();
     vector<int> getLnbHandles();
     DemuxCapabilities getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps);
-    FrontendInfo FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
+    FrontendInfo frontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
     void updateTunerResources();
     void updateFrontendResources();
     void updateLnbResources();
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 3b054e9..a746139 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -1368,7 +1368,7 @@
     public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
         try {
             return mService.getDefaultNetworkCapabilitiesForUser(
-                    userId, mContext.getOpPackageName());
+                    userId, mContext.getOpPackageName(), getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1450,7 +1450,8 @@
     @Nullable
     public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
         try {
-            return mService.getNetworkCapabilities(network, mContext.getOpPackageName());
+            return mService.getNetworkCapabilities(
+                    network, mContext.getOpPackageName(), getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3735,7 +3736,8 @@
                 Binder binder = new Binder();
                 if (reqType == LISTEN) {
                     request = mService.listenForNetwork(
-                            need, messenger, binder, callingPackageName);
+                            need, messenger, binder, callingPackageName,
+                            getAttributionTag());
                 } else {
                     request = mService.requestNetwork(
                             need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType,
@@ -4180,7 +4182,8 @@
         checkPendingIntentNotNull(operation);
         try {
             mService.pendingListenForNetwork(
-                    request.networkCapabilities, operation, mContext.getOpPackageName());
+                    request.networkCapabilities, operation, mContext.getOpPackageName(),
+                    getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index e2672c4..f909d13 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -66,7 +66,7 @@
     Network getNetworkForType(int networkType);
     Network[] getAllNetworks();
     NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
-            int userId, String callingPackageName);
+            int userId, String callingPackageName, String callingAttributionTag);
 
     boolean isNetworkSupported(int networkType);
 
@@ -75,7 +75,8 @@
     LinkProperties getLinkPropertiesForType(int networkType);
     LinkProperties getLinkProperties(in Network network);
 
-    NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName);
+    NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName,
+            String callingAttributionTag);
 
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     NetworkState[] getAllNetworkState();
@@ -176,10 +177,12 @@
     void releasePendingNetworkRequest(in PendingIntent operation);
 
     NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
-            in Messenger messenger, in IBinder binder, String callingPackageName);
+            in Messenger messenger, in IBinder binder, String callingPackageName,
+            String callingAttributionTag);
 
     void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
-            in PendingIntent operation, String callingPackageName);
+            in PendingIntent operation, String callingPackageName,
+            String callingAttributionTag);
 
     void releaseNetworkRequest(in NetworkRequest networkRequest);
 
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index b9ef4c2..6540397 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -16,6 +16,22 @@
 
 package android.net;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -30,6 +46,8 @@
 import android.text.TextUtils;
 import android.util.proto.ProtoOutputStream;
 
+import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
@@ -156,8 +174,30 @@
      * needed in terms of {@link NetworkCapabilities} features
      */
     public static class Builder {
+        /**
+         * Capabilities that are currently compatible with VCN networks.
+         */
+        private static final List<Integer> VCN_SUPPORTED_CAPABILITIES = Arrays.asList(
+                NET_CAPABILITY_CAPTIVE_PORTAL,
+                NET_CAPABILITY_DUN,
+                NET_CAPABILITY_FOREGROUND,
+                NET_CAPABILITY_INTERNET,
+                NET_CAPABILITY_NOT_CONGESTED,
+                NET_CAPABILITY_NOT_METERED,
+                NET_CAPABILITY_NOT_RESTRICTED,
+                NET_CAPABILITY_NOT_ROAMING,
+                NET_CAPABILITY_NOT_SUSPENDED,
+                NET_CAPABILITY_NOT_VPN,
+                NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+                NET_CAPABILITY_TEMPORARILY_NOT_METERED,
+                NET_CAPABILITY_TRUSTED,
+                NET_CAPABILITY_VALIDATED);
+
         private final NetworkCapabilities mNetworkCapabilities;
 
+        // A boolean that represents the user modified NOT_VCN_MANAGED capability.
+        private boolean mModifiedNotVcnManaged = false;
+
         /**
          * Default constructor for Builder.
          */
@@ -179,6 +219,7 @@
             // maybeMarkCapabilitiesRestricted() doesn't add back.
             final NetworkCapabilities nc = new NetworkCapabilities(mNetworkCapabilities);
             nc.maybeMarkCapabilitiesRestricted();
+            deduceNotVcnManagedCapability(nc);
             return new NetworkRequest(nc, ConnectivityManager.TYPE_NONE,
                     ConnectivityManager.REQUEST_ID_UNSET, Type.NONE);
         }
@@ -195,6 +236,9 @@
          */
         public Builder addCapability(@NetworkCapabilities.NetCapability int capability) {
             mNetworkCapabilities.addCapability(capability);
+            if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) {
+                mModifiedNotVcnManaged = true;
+            }
             return this;
         }
 
@@ -206,6 +250,9 @@
          */
         public Builder removeCapability(@NetworkCapabilities.NetCapability int capability) {
             mNetworkCapabilities.removeCapability(capability);
+            if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) {
+                mModifiedNotVcnManaged = true;
+            }
             return this;
         }
 
@@ -263,6 +310,9 @@
         @NonNull
         public Builder clearCapabilities() {
             mNetworkCapabilities.clearAll();
+            // If the caller explicitly clear all capabilities, the NOT_VCN_MANAGED capabilities
+            // should not be add back later.
+            mModifiedNotVcnManaged = true;
             return this;
         }
 
@@ -382,6 +432,25 @@
             mNetworkCapabilities.setSignalStrength(signalStrength);
             return this;
         }
+
+        /**
+         * Deduce the NET_CAPABILITY_NOT_VCN_MANAGED capability from other capabilities
+         * and user intention, which includes:
+         *   1. For the requests that don't have anything besides
+         *      {@link #VCN_SUPPORTED_CAPABILITIES}, add the NET_CAPABILITY_NOT_VCN_MANAGED to
+         *      allow the callers automatically utilize VCN networks if available.
+         *   2. For the requests that explicitly add or remove NET_CAPABILITY_NOT_VCN_MANAGED,
+         *      do not alter them to allow user fire request that suits their need.
+         *
+         * @hide
+         */
+        private void deduceNotVcnManagedCapability(final NetworkCapabilities nc) {
+            if (mModifiedNotVcnManaged) return;
+            for (final int cap : nc.getCapabilities()) {
+                if (!VCN_SUPPORTED_CAPABILITIES.contains(cap)) return;
+            }
+            nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+        }
     }
 
     // implement the Parcelable interface
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index b8030f1..4c7b898 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -259,7 +259,12 @@
                 .append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode).append(',')
                 .append("dataState=").append(dataState).append(',')
                 .append("serviceState=").append(serviceState == null ? ""
-                        : serviceState.toString()).append(',')
+                        : "mVoiceRegState=" + serviceState.getState() + "("
+                                + ServiceState.rilServiceStateToString(serviceState.getState())
+                                + ")" + ", mDataRegState=" + serviceState.getDataRegState() + "("
+                                + ServiceState.rilServiceStateToString(
+                                        serviceState.getDataRegState()) + ")")
+                                        .append(',')
                 .append("signalStrength=").append(signalStrength == null ? ""
                         : signalStrength.toString()).append(',')
                 .append("telephonyDisplayInfo=").append(telephonyDisplayInfo == null ? ""
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index ad6a531..66165b6 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -147,10 +147,5 @@
         VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.NOTIFICATION_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(
-                Global.ONE_HANDED_KEYGUARD_SIDE,
-                new InclusiveIntegerRangeValidator(
-                        /* first= */Global.ONE_HANDED_KEYGUARD_SIDE_LEFT,
-                        /* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT));
     }
 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c11877a..438cec8 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -283,7 +283,6 @@
                     Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
                     Settings.Global.EUICC_SWITCH_SLOT_TIMEOUT_MILLIS,
                     Settings.Global.FANCY_IME_ANIMATIONS,
-                    Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
                     Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
                     Settings.Global.FORCED_APP_STANDBY_ENABLED,
                     Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 9021864..7bfb42b 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -76,8 +76,8 @@
         "androidx.dynamicanimation_dynamicanimation",
         "androidx-constraintlayout_constraintlayout",
         "androidx.exifinterface_exifinterface",
-        "kotlinx-coroutines-android",
-        "kotlinx-coroutines-core",
+        "kotlinx_coroutines_android",
+        "kotlinx_coroutines",
         "iconloader_base",
         "SystemUI-tags",
         "SystemUI-proto",
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
index b5f55af..7986809 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
@@ -24,7 +24,7 @@
     <include
         style="@style/BouncerSecurityContainer"
         layout="@layout/keyguard_host_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
 </FrameLayout>
 
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
index c75ee51..04e645b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
@@ -41,14 +41,13 @@
         android:layout_gravity="center">
         <com.android.keyguard.KeyguardSecurityViewFlipper
             android:id="@+id/view_flipper"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
             android:clipChildren="false"
             android:clipToPadding="false"
             android:paddingTop="@dimen/keyguard_security_view_top_margin"
             android:paddingStart="@dimen/keyguard_security_view_lateral_margin"
             android:paddingEnd="@dimen/keyguard_security_view_lateral_margin"
-            android:layout_gravity="center"
             android:gravity="center">
         </com.android.keyguard.KeyguardSecurityViewFlipper>
     </com.android.keyguard.KeyguardSecurityContainer>
diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml
index 6176f7c..8d9d6ee 100644
--- a/packages/SystemUI/res-keyguard/values/config.xml
+++ b/packages/SystemUI/res-keyguard/values/config.xml
@@ -22,5 +22,4 @@
 
     <!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
     <bool name="config_disableMenuKeyInLockScreen">false</bool>
-    <bool name="can_use_one_handed_bouncer">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 01e54ff..ded8a2e 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -34,6 +34,9 @@
 
     <bool name="flag_brightness_slider">false</bool>
 
+    <!-- The new animations to/from lockscreen and AOD! -->
+    <bool name="flag_lockscreen_animations">false</bool>
+
     <!-- People Tile flag -->
     <bool name="flag_conversations">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d932395..589a39c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -954,11 +954,8 @@
     <string name="quick_settings_dark_mode_secondary_label_on_at">On at <xliff:g id="time" example="10 pm">%s</xliff:g></string>
     <!-- QuickSettings: Secondary text for when the Dark theme or some other tile will be on until some user-selected time. [CHAR LIMIT=20] -->
     <string name="quick_settings_dark_mode_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string>
-    <!-- TODO(b/170970602): remove translatable=false when RBC has official name and strings -->
-    <!-- QuickSettings: Label for the toggle that controls whether Reduce Bright Colors is enabled. [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_reduce_bright_colors_label" translatable="false">Reduce Bright Colors</string>
-        <!-- QuickSettings: Secondary text for intensity level of Reduce Bright Colors. [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_reduce_bright_colors_secondary_label" translatable="false"> <xliff:g id="intensity" example="50">%d</xliff:g>%% reduction</string>
+    <!-- QuickSettings: Label for the toggle that controls whether Reduce Brightness is enabled. [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_reduce_bright_colors_label">Reduce Brightness</string>
 
     <!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
     <string name="quick_settings_nfc_label">NFC</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index 19e7d7e..bec9220 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -17,7 +17,6 @@
 package com.android.systemui.shared.system;
 
 import android.annotation.IntDef;
-import android.annotation.NonNull;
 import android.view.View;
 
 import com.android.internal.jank.InteractionJankMonitor;
@@ -37,6 +36,10 @@
             InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP;
     public static final int CUJ_QUICK_SWITCH =
             InteractionJankMonitor.CUJ_LAUNCHER_QUICK_SWITCH;
+    public static final int CUJ_OPEN_ALL_APPS =
+            InteractionJankMonitor.CUJ_LAUNCHER_OPEN_ALL_APPS;
+    public static final int CUJ_ALL_APPS_SCROLL =
+            InteractionJankMonitor.CUJ_LAUNCHER_ALL_APPS_SCROLL;
 
     @IntDef({
             CUJ_APP_LAUNCH_FROM_RECENTS,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index 70021b6..fbabaa4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -114,10 +114,13 @@
                 for (int i = params.length - 1; i >= 0; i--) {
                     SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams =
                             params[i];
-                    t.deferTransactionUntil(surfaceParams.surface, mBarrierSurfaceControl, frame);
                     surfaceParams.applyTo(t);
                 }
-                t.apply();
+                if (mTargetViewRootImpl != null) {
+                    mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
+                } else {
+                    t.apply();
+                }
                 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                 Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
                         .sendToTarget();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
index 4a28d56..89c60f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
@@ -56,4 +56,12 @@
                     });
         }
     }
+
+    public void mergeWithNextTransaction(SurfaceControl.Transaction t, long frame) {
+        if (mViewRoot != null) {
+            mViewRoot.mergeWithNextTransaction(t, frame);
+        } else {
+            t.apply();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index c182fd1..5f6fd30 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -29,17 +29,12 @@
 import android.content.Context;
 import android.graphics.Insets;
 import android.graphics.Rect;
-import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.MathUtils;
 import android.util.TypedValue;
-import android.view.Gravity;
 import android.view.MotionEvent;
-import android.view.OrientationEventListener;
 import android.view.VelocityTracker;
-import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewPropertyAnimator;
 import android.view.WindowInsets;
 import android.view.WindowInsetsAnimation;
 import android.view.WindowInsetsAnimationControlListener;
@@ -60,7 +55,6 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
 import java.util.List;
 
@@ -105,12 +99,6 @@
     private boolean mDisappearAnimRunning;
     private SwipeListener mSwipeListener;
 
-    private boolean mIsSecurityViewLeftAligned = true;
-    private boolean mOneHandedMode = false;
-    private SecurityMode mSecurityMode = SecurityMode.Invalid;
-    private ViewPropertyAnimator mRunningOneHandedAnimator;
-    private final OrientationEventListener mOrientationEventListener;
-
     private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
             new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
 
@@ -169,20 +157,16 @@
     // Used to notify the container when something interesting happens.
     public interface SecurityCallback {
         boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
-
         void userActivity();
-
         void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
 
         /**
-         * @param strongAuth   wheher the user has authenticated with strong authentication like
-         *                     pattern, password or PIN but not by trust agents or fingerprint
+         * @param strongAuth wheher the user has authenticated with strong authentication like
+         *                   pattern, password or PIN but not by trust agents or fingerprint
          * @param targetUserId a user that needs to be the foreground user at the finish completion.
          */
         void finish(boolean strongAuth, int targetUserId);
-
         void reset();
-
         void onCancelClicked();
     }
 
@@ -240,136 +224,12 @@
         super(context, attrs, defStyle);
         mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
         mViewConfiguration = ViewConfiguration.get(context);
-
-        mOrientationEventListener = new OrientationEventListener(context) {
-            @Override
-            public void onOrientationChanged(int orientation) {
-                updateLayoutForSecurityMode(mSecurityMode);
-            }
-        };
     }
 
     void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
-        mSecurityMode = securityMode;
         mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
         updateBiometricRetry(securityMode, faceAuthEnabled);
 
-        updateLayoutForSecurityMode(securityMode);
-        mOrientationEventListener.enable();
-    }
-
-    void updateLayoutForSecurityMode(SecurityMode securityMode) {
-        mSecurityMode = securityMode;
-        mOneHandedMode = canUseOneHandedBouncer();
-
-        if (mOneHandedMode) {
-            mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext);
-        }
-
-        updateSecurityViewGravity();
-        updateSecurityViewLocation(false);
-    }
-
-    /** Return whether the one-handed keyguard should be enabled. */
-    private boolean canUseOneHandedBouncer() {
-        // Is it enabled?
-        if (!getResources().getBoolean(
-                com.android.internal.R.bool.config_enableOneHandedKeyguard)) {
-            return false;
-        }
-
-        if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) {
-            return false;
-        }
-
-        return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
-    }
-
-    /** Read whether the one-handed keyguard should be on the left/right from settings. */
-    private boolean isOneHandedKeyguardLeftAligned(Context context) {
-        try {
-            return Settings.Global.getInt(context.getContentResolver(),
-                    Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
-                    == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
-        } catch (Settings.SettingNotFoundException ex) {
-            return true;
-        }
-    }
-
-    private void updateSecurityViewGravity() {
-        View securityView = findKeyguardSecurityView();
-
-        if (securityView == null) {
-            return;
-        }
-
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
-
-        if (mOneHandedMode) {
-            lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
-        } else {
-            lp.gravity = Gravity.CENTER;
-        }
-
-        securityView.setLayoutParams(lp);
-    }
-
-    /**
-     * Moves the inner security view to the correct location (in one handed mode) with animation.
-     * This is triggered when the user taps on the side of the screen that is not currently occupied
-     * by the security view .
-     */
-    private void updateSecurityViewLocation(boolean animate) {
-        View securityView = findKeyguardSecurityView();
-
-        if (securityView == null) {
-            return;
-        }
-
-        if (!mOneHandedMode) {
-            securityView.setTranslationX(0);
-            return;
-        }
-
-        if (mRunningOneHandedAnimator != null) {
-            mRunningOneHandedAnimator.cancel();
-            mRunningOneHandedAnimator = null;
-        }
-
-        int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
-
-        if (animate) {
-            mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation);
-            mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-            mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mRunningOneHandedAnimator = null;
-                }
-            });
-
-            mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-            mRunningOneHandedAnimator.start();
-        } else {
-            securityView.setTranslationX(targetTranslation);
-        }
-    }
-
-    @Nullable
-    private KeyguardSecurityViewFlipper findKeyguardSecurityView() {
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-
-            if (isKeyguardSecurityView(child)) {
-                return (KeyguardSecurityViewFlipper) child;
-            }
-        }
-
-        return null;
-    }
-
-    private boolean isKeyguardSecurityView(View view) {
-        return view instanceof KeyguardSecurityViewFlipper;
     }
 
     public void onPause() {
@@ -378,7 +238,6 @@
             mAlertDialog = null;
         }
         mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
-        mOrientationEventListener.disable();
     }
 
     @Override
@@ -460,44 +319,19 @@
                 if (mSwipeListener != null) {
                     mSwipeListener.onSwipeUp();
                 }
-            } else {
-                if (!mIsDragging) {
-                    handleTap(event);
-                }
             }
         }
         return true;
     }
 
-    private void handleTap(MotionEvent event) {
-        // If we're using a fullscreen security mode, skip
-        if (!mOneHandedMode) {
-            return;
-        }
-
-        // Did the tap hit the "other" side of the bouncer?
-        if ((mIsSecurityViewLeftAligned && (event.getX() > getWidth() / 2f))
-                || (!mIsSecurityViewLeftAligned && (event.getX() < getWidth() / 2f))) {
-            mIsSecurityViewLeftAligned = !mIsSecurityViewLeftAligned;
-
-            Settings.Global.putInt(
-                    mContext.getContentResolver(),
-                    Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
-                    mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
-                            : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
-
-            updateSecurityViewLocation(true);
-        }
-    }
-
     void setSwipeListener(SwipeListener swipeListener) {
         mSwipeListener = swipeListener;
     }
 
     private void startSpringAnimation(float startVelocity) {
         mSpringAnimation
-                .setStartVelocity(startVelocity)
-                .animateToFinalPosition(0);
+            .setStartVelocity(startVelocity)
+            .animateToFinalPosition(0);
     }
 
     public void startDisappearAnimation(SecurityMode securitySelection) {
@@ -607,17 +441,18 @@
         return insets.inset(0, 0, 0, inset);
     }
 
+
     private void showDialog(String title, String message) {
         if (mAlertDialog != null) {
             mAlertDialog.dismiss();
         }
 
         mAlertDialog = new AlertDialog.Builder(mContext)
-                .setTitle(title)
-                .setMessage(message)
-                .setCancelable(false)
-                .setNeutralButton(R.string.ok, null)
-                .create();
+            .setTitle(title)
+            .setMessage(message)
+            .setCancelable(false)
+            .setNeutralButton(R.string.ok, null)
+            .create();
         if (!(mContext instanceof Activity)) {
             mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
         }
@@ -655,44 +490,6 @@
         }
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int maxHeight = 0;
-        int maxWidth = 0;
-        int childState = 0;
-
-        int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                MeasureSpec.getSize(widthMeasureSpec) / 2,
-                MeasureSpec.getMode(widthMeasureSpec));
-
-        for (int i = 0; i < getChildCount(); i++) {
-            final View view = getChildAt(i);
-            if (view.getVisibility() != GONE) {
-                if (mOneHandedMode && isKeyguardSecurityView(view)) {
-                    measureChildWithMargins(view, halfWidthMeasureSpec, 0,
-                            heightMeasureSpec, 0);
-                } else {
-                    measureChildWithMargins(view, widthMeasureSpec, 0,
-                            heightMeasureSpec, 0);
-                }
-                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-                maxWidth = Math.max(maxWidth,
-                        view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
-                maxHeight = Math.max(maxHeight,
-                        view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
-                childState = combineMeasuredStates(childState, view.getMeasuredState());
-            }
-        }
-
-        // Check against our minimum height and width
-        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
-        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
-
-        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
-                resolveSizeAndState(maxHeight, heightMeasureSpec,
-                        childState << MEASURED_HEIGHT_STATE_SHIFT));
-    }
-
     void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
         String message = null;
         switch (userType) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index fdab8db..1a8d420 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -404,7 +404,6 @@
         if (newView != null) {
             newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
             mSecurityViewFlipperController.show(newView);
-            mView.updateLayoutForSecurityMode(securityMode);
         }
 
         mSecurityCallback.onSecurityModeChanged(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index 631c248..c77c867 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -92,13 +92,4 @@
                 throw new IllegalStateException("Unknown security quality:" + security);
         }
     }
-
-    /**
-     * Returns whether the given security view should be used in a "one handed" way. This can be
-     * used to change how the security view is drawn (e.g. take up less of the screen, and align to
-     * one side).
-     */
-    public static boolean isSecurityViewOneHanded(SecurityMode securityMode) {
-        return securityMode == SecurityMode.Pattern || securityMode == SecurityMode.PIN;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 062410a..ffb8446 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -33,7 +33,7 @@
 import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
 
 import java.util.Optional;
 
@@ -86,7 +86,7 @@
         Builder setShellCommandHandler(Optional<ShellCommandHandler> shellDump);
 
         @BindsInstance
-        Builder setTransitions(Transitions t);
+        Builder setTransitions(RemoteTransitions t);
 
         SysUIComponent build();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 60b665f..84dd259 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -27,7 +27,7 @@
 import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
 
 import java.util.Optional;
 
@@ -55,16 +55,12 @@
         getShellInit().init();
     }
 
-    // Gets the Shell init instance
     @WMSingleton
     ShellInit getShellInit();
 
-    // Gets the Shell dump instance
     @WMSingleton
     Optional<ShellCommandHandler> getShellCommandHandler();
 
-    // TODO(b/162923491): We currently pass the instances through to SysUI, but that may change
-    //                    depending on the threading mechanism we go with
     @WMSingleton
     Optional<OneHanded> getOneHanded();
 
@@ -89,7 +85,6 @@
     @WMSingleton
     Optional<TaskViewFactory> getTaskViewFactory();
 
-    /** Gets transitions */
     @WMSingleton
-    Transitions getTransitions();
+    RemoteTransitions getTransitions();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
index 9ab2d73..35a8257 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.qs.dagger;
 
+import android.content.Context;
+import android.hardware.display.ColorDisplayManager;
+
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.FeatureFlags;
 
@@ -27,6 +30,7 @@
 @Module
 public interface QSFlagsModule {
     String QS_LABELS_FLAG = "qs_labels_flag";
+    String RBC_AVAILABLE = "rbc_available";
 
     @Provides
     @SysUISingleton
@@ -34,4 +38,12 @@
     static boolean provideQSFlag(FeatureFlags featureFlags) {
         return featureFlags.isQSLabelsEnabled();
     }
+
+    /** */
+    @Provides
+    @SysUISingleton
+    @Named(RBC_AVAILABLE)
+    static boolean isReduceBrightColorsAvailable(Context context) {
+        return ColorDisplayManager.isReduceBrightColorsAvailable(context);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index 84c7611..f94cabc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -16,12 +16,13 @@
 
 package com.android.systemui.qs.tiles;
 
+import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
+
 import android.content.Intent;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
-import android.text.TextUtils;
 import android.widget.Switch;
 
 import com.android.internal.logging.MetricsLogger;
@@ -39,6 +40,7 @@
 import com.android.systemui.util.settings.SecureSettings;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
 /** Quick settings tile: Reduce Bright Colors **/
 public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> {
@@ -46,9 +48,11 @@
     //TODO(b/170973645): get icon drawable
     private final Icon mIcon = null;
     private final SecureSetting mActivatedSetting;
+    private final boolean mIsAvailable;
 
     @Inject
     public ReduceBrightColorsTile(
+            @Named(RBC_AVAILABLE) boolean isAvailable,
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
@@ -69,11 +73,12 @@
                 refreshState();
             }
         };
+        mIsAvailable = isAvailable;
+
     }
     @Override
     public boolean isAvailable() {
-        // TODO(b/170970675): Call into ColorDisplayService to get availability/config status
-        return true;
+        return mIsAvailable;
     }
 
     @Override
@@ -121,15 +126,6 @@
         state.label = mContext.getString(R.string.quick_settings_reduce_bright_colors_label);
         state.expandedAccessibilityClassName = Switch.class.getName();
         state.contentDescription = state.label;
-
-        final int intensity = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mActivatedSetting.getCurrentUser());
-        state.secondaryLabel = state.value ? mContext.getString(
-                R.string.quick_settings_reduce_bright_colors_secondary_label, intensity) : "";
-
-        state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
-                ? state.label
-                : TextUtils.concat(state.label, ", ", state.secondaryLabel);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index a9f76f6..01a8c1c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -101,7 +101,7 @@
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -149,7 +149,7 @@
     private final ScreenshotHelper mScreenshotHelper;
     private final Optional<OneHanded> mOneHandedOptional;
     private final CommandQueue mCommandQueue;
-    private final Transitions mShellTransitions;
+    private final RemoteTransitions mShellTransitions;
 
     private Region mActiveNavBarRegion;
 
@@ -799,7 +799,7 @@
             Optional<Lazy<StatusBar>> statusBarOptionalLazy,
             Optional<OneHanded> oneHandedOptional,
             BroadcastDispatcher broadcastDispatcher,
-            Transitions shellTransitions) {
+            RemoteTransitions shellTransitions) {
         super(broadcastDispatcher);
         mContext = context;
         mPipOptional = pipOptional;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index a3b5f27..43bb343 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -79,6 +79,7 @@
     private final float mMaximumBacklightForVr;
     private final float mDefaultBacklightForVr;
 
+    private final int mDisplayId;
     private final Context mContext;
     private final ToggleSlider mControl;
     private final boolean mAutomaticAvailable;
@@ -311,6 +312,7 @@
         };
         mBrightnessObserver = new BrightnessObserver(mHandler);
 
+        mDisplayId = mContext.getDisplayId();
         PowerManager pm = context.getSystemService(PowerManager.class);
         mMinimumBacklight = pm.getBrightnessConstraint(
                 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
@@ -420,7 +422,7 @@
     }
 
     private void setBrightness(float brightness) {
-        mDisplayManager.setTemporaryBrightness(brightness);
+        mDisplayManager.setTemporaryBrightness(mDisplayId, brightness);
     }
 
     private void updateVrMode(boolean isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index e7b60c3..2dd85e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -67,6 +67,10 @@
         return mFlagReader.isEnabled(R.bool.flag_brightness_slider);
     }
 
+    public boolean useNewLockscreenAnimations() {
+        return mFlagReader.isEnabled(R.bool.flag_lockscreen_animations);
+    }
+
     public boolean isPeopleTileEnabled() {
         return mFlagReader.isEnabled(R.bool.flag_conversations);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 2f0f90d..c1feaca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -11,14 +11,10 @@
 import android.graphics.PorterDuffXfermode
 import android.graphics.RadialGradient
 import android.graphics.Shader
-import android.os.SystemProperties
 import android.util.AttributeSet
 import android.view.View
 import com.android.systemui.Interpolators
 
-val enableLightReveal =
-        SystemProperties.getBoolean("persist.sysui.show_new_screen_on_transitions", false)
-
 /**
  * Provides methods to modify the various properties of a [LightRevealScrim] to reveal between 0% to
  * 100% of the view(s) underneath the scrim.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index a816ecc..cfadcd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -388,7 +388,28 @@
      */
     public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input,
             PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo) {
+        return activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo,
+                null /* userMessageContent */, null /* authBypassCheck */);
+    }
 
+    /**
+     * Activates a given {@link RemoteInput}
+     *
+     * @param view The view of the action button or suggestion chip that was tapped.
+     * @param inputs The remote inputs that need to be sent to the app.
+     * @param input The remote input that needs to be activated.
+     * @param pendingIntent The pending intent to be sent to the app.
+     * @param editedSuggestionInfo The smart reply that should be inserted in the remote input, or
+     *         {@code null} if the user is not editing a smart reply.
+     * @param userMessageContent User-entered text with which to initialize the remote input view.
+     * @param authBypassCheck Optional auth bypass check associated with this remote input
+     *         activation. If {@code null}, we never bypass.
+     * @return Whether the {@link RemoteInput} was activated.
+     */
+    public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input,
+            PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo,
+            @Nullable String userMessageContent,
+            @Nullable AuthBypassPredicate authBypassCheck) {
         ViewParent p = view.getParent();
         RemoteInputView riv = null;
         ExpandableNotificationRow row = null;
@@ -410,40 +431,9 @@
 
         row.setUserExpanded(true);
 
-        if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
-            final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
-
-            final boolean isLockedManagedProfile =
-                    mUserManager.getUserInfo(userId).isManagedProfile()
-                    && mKeyguardManager.isDeviceLocked(userId);
-
-            final boolean isParentUserLocked;
-            if (isLockedManagedProfile) {
-                final UserInfo profileParent = mUserManager.getProfileParent(userId);
-                isParentUserLocked = (profileParent != null)
-                        && mKeyguardManager.isDeviceLocked(profileParent.id);
-            } else {
-                isParentUserLocked = false;
-            }
-
-            if (mLockscreenUserManager.isLockscreenPublicMode(userId)
-                    || mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
-                // If the parent user is no longer locked, and the user to which the remote input
-                // is destined is a locked, managed profile, then onLockedWorkRemoteInput should be
-                // called to unlock it.
-                if (isLockedManagedProfile && !isParentUserLocked) {
-                    mCallback.onLockedWorkRemoteInput(userId, row, view);
-                } else {
-                    // Even if we don't have security we should go through this flow, otherwise
-                    // we won't go to the shade.
-                    mCallback.onLockedRemoteInput(row, view);
-                }
-                return true;
-            }
-            if (isLockedManagedProfile) {
-                mCallback.onLockedWorkRemoteInput(userId, row, view);
-                return true;
-            }
+        final boolean deferBouncer = authBypassCheck != null;
+        if (!deferBouncer && showBouncerForRemoteInput(view, pendingIntent, row)) {
+            return true;
         }
 
         if (riv != null && !riv.isAttachedToWindow()) {
@@ -461,7 +451,10 @@
                 && !row.getPrivateLayout().getExpandedChild().isShown()) {
             // The expanded layout is selected, but it's not shown yet, let's wait on it to
             // show before we do the animation.
-            mCallback.onMakeExpandedVisibleForRemoteInput(row, view);
+            mCallback.onMakeExpandedVisibleForRemoteInput(row, view, deferBouncer, () -> {
+                activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo,
+                        userMessageContent, authBypassCheck);
+            });
             return true;
         }
 
@@ -491,10 +484,62 @@
         riv.setPendingIntent(pendingIntent);
         riv.setRemoteInput(inputs, input, editedSuggestionInfo);
         riv.focusAnimated();
+        if (userMessageContent != null) {
+            riv.setEditTextContent(userMessageContent);
+        }
+        if (deferBouncer) {
+            final ExpandableNotificationRow finalRow = row;
+            riv.setBouncerChecker(() -> !authBypassCheck.canSendRemoteInputWithoutBouncer()
+                    && showBouncerForRemoteInput(view, pendingIntent, finalRow));
+        }
 
         return true;
     }
 
+    private boolean showBouncerForRemoteInput(View view, PendingIntent pendingIntent,
+            ExpandableNotificationRow row) {
+        if (mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
+            return false;
+        }
+
+        final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
+
+        final boolean isLockedManagedProfile =
+                mUserManager.getUserInfo(userId).isManagedProfile()
+                        && mKeyguardManager.isDeviceLocked(userId);
+
+        final boolean isParentUserLocked;
+        if (isLockedManagedProfile) {
+            final UserInfo profileParent = mUserManager.getProfileParent(userId);
+            isParentUserLocked = (profileParent != null)
+                    && mKeyguardManager.isDeviceLocked(profileParent.id);
+        } else {
+            isParentUserLocked = false;
+        }
+
+        if ((mLockscreenUserManager.isLockscreenPublicMode(userId)
+                || mStatusBarStateController.getState() == StatusBarState.KEYGUARD)) {
+            // If the parent user is no longer locked, and the user to which the remote
+            // input
+            // is destined is a locked, managed profile, then onLockedWorkRemoteInput
+            // should be
+            // called to unlock it.
+            if (isLockedManagedProfile && !isParentUserLocked) {
+                mCallback.onLockedWorkRemoteInput(userId, row, view);
+            } else {
+                // Even if we don't have security we should go through this flow, otherwise
+                // we won't go to the shade.
+                mCallback.onLockedRemoteInput(row, view);
+            }
+            return true;
+        }
+        if (isLockedManagedProfile) {
+            mCallback.onLockedWorkRemoteInput(userId, row, view);
+            return true;
+        }
+        return false;
+    }
+
     private RemoteInputView findRemoteInputView(View v) {
         if (v == null) {
             return null;
@@ -807,8 +852,11 @@
          *
          * @param row
          * @param clickedView
+         * @param deferBouncer
+         * @param runnable
          */
-        void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView);
+        void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView,
+                boolean deferBouncer, Runnable runnable);
 
         /**
          * Return whether or not remote input should be handled for this view.
@@ -845,4 +893,28 @@
          */
         boolean handleClick();
     }
+
+    /**
+     * Predicate that is associated with a specific {@link #activateRemoteInput(View, RemoteInput[],
+     * RemoteInput, PendingIntent, EditedSuggestionInfo, String, AuthBypassPredicate)}
+     * invocation that determines whether or not the bouncer can be bypassed when sending the
+     * RemoteInput.
+     */
+    public interface AuthBypassPredicate {
+        /**
+         * Determines if the RemoteInput can be sent without the bouncer. Should be checked the
+         * same frame that the RemoteInput is to be sent.
+         */
+        boolean canSendRemoteInputWithoutBouncer();
+    }
+
+    /** Shows the bouncer if necessary */
+    public interface BouncerChecker {
+        /**
+         * Shows the bouncer if necessary in order to send a RemoteInput.
+         *
+         * @return {@code true} if the bouncer was shown, {@code false} otherwise
+         */
+        boolean showBouncerIfNecessary();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
index 7964845..301c372 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
@@ -29,6 +29,30 @@
 
     private View mWrappedView = null;
 
+    /**
+     * Determines if the standard template contains a custom view, injected by Notification.Builder
+     */
+    public static boolean hasCustomView(View v) {
+        return getWrappedCustomView(v) != null;
+    }
+
+    private static View getWrappedCustomView(View view) {
+        if (view == null) {
+            return null;
+        }
+        ViewGroup container = view.findViewById(
+                com.android.internal.R.id.notification_main_column);
+        if (container == null) {
+            return null;
+        }
+        Integer childIndex = (Integer) container.getTag(
+                com.android.internal.R.id.notification_custom_view_index_tag);
+        if (childIndex == null || childIndex == -1) {
+            return null;
+        }
+        return container.getChildAt(childIndex);
+    }
+
     protected NotificationDecoratedCustomViewWrapper(Context ctx, View view,
             ExpandableNotificationRow row) {
         super(ctx, view, row);
@@ -36,13 +60,7 @@
 
     @Override
     public void onContentUpdated(ExpandableNotificationRow row) {
-        ViewGroup container = mView.findViewById(
-                com.android.internal.R.id.notification_main_column);
-        Integer childIndex = (Integer) container.getTag(
-                com.android.internal.R.id.notification_custom_view_index_tag);
-        if (childIndex != null && childIndex != -1) {
-            mWrappedView = container.getChildAt(childIndex);
-        }
+        mWrappedView = getWrappedCustomView(mView);
 
         // Custom views will most likely use just white or black as their text color.
         // We need to scan through and replace these colors by Material NEXT colors.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 7b5c5f6..b3d1a94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -80,6 +80,9 @@
             if (Notification.DecoratedCustomViewStyle.class.equals(style)) {
                 return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
             }
+            if (NotificationDecoratedCustomViewWrapper.hasCustomView(v)) {
+                return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
+            }
             return new NotificationTemplateViewWrapper(ctx, v, row);
         } else if (v instanceof NotificationHeaderView) {
             return new NotificationHeaderViewWrapper(ctx, v, row);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 8c2fa33..85d8df8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.tuner.TunerService;
 
@@ -54,6 +55,7 @@
     private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
     private final Resources mResources;
     private final BatteryController mBatteryController;
+    private final FeatureFlags mFeatureFlags;
 
     private boolean mDozeAlwaysOn;
     private boolean mControlScreenOffAnimation;
@@ -65,7 +67,8 @@
             AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
             PowerManager powerManager,
             BatteryController batteryController,
-            TunerService tunerService) {
+            TunerService tunerService,
+            FeatureFlags featureFlags) {
         mResources = resources;
         mAmbientDisplayConfiguration = ambientDisplayConfiguration;
         mAlwaysOnPolicy = alwaysOnDisplayPolicy;
@@ -74,6 +77,7 @@
         mControlScreenOffAnimation = !getDisplayNeedsBlanking();
         mPowerManager = powerManager;
         mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
+        mFeatureFlags = featureFlags;
 
         tunerService.addTunable(
                 this,
@@ -200,8 +204,7 @@
      * then abruptly showing AOD.
      */
     public boolean shouldControlUnlockedScreenOff() {
-        return getAlwaysOn() && SystemProperties.getBoolean(
-                "persist.sysui.show_new_screen_on_transitions", false);
+        return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations();
     }
 
     private boolean getBoolean(String propName, int resId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 7095afd..061f2a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -35,7 +35,6 @@
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
-import static com.android.systemui.statusbar.LightRevealScrimKt.getEnableLightReveal;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -181,6 +180,7 @@
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -442,6 +442,7 @@
     private final KeyguardViewMediator mKeyguardViewMediator;
     protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private final BrightnessSlider.Factory mBrightnessSliderFactory;
+    private final FeatureFlags mFeatureFlags;
 
     private final List<ExpansionChangedListener> mExpansionChangedListeners;
 
@@ -765,7 +766,8 @@
             Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
             NotificationIconAreaController notificationIconAreaController,
-            BrightnessSlider.Factory brightnessSliderFactory) {
+            BrightnessSlider.Factory brightnessSliderFactory,
+            FeatureFlags featureFlags) {
         super(context);
         mNotificationsController = notificationsController;
         mLightBarController = lightBarController;
@@ -843,6 +845,7 @@
         mDemoModeController = demoModeController;
         mNotificationIconAreaController = notificationIconAreaController;
         mBrightnessSliderFactory = brightnessSliderFactory;
+        mFeatureFlags = featureFlags;
 
         mExpansionChangedListeners = new ArrayList<>();
 
@@ -1186,9 +1189,11 @@
 
         mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
 
-        if (getEnableLightReveal()) {
+        if (mFeatureFlags.useNewLockscreenAnimations() && mDozeParameters.getAlwaysOn()) {
             mLightRevealScrim.setVisibility(View.VISIBLE);
             mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
+        } else {
+            mLightRevealScrim.setVisibility(View.GONE);
         }
 
         mNotificationPanelViewController.initDependencies(
@@ -3651,7 +3656,7 @@
 
     @Override
     public void onDozeAmountChanged(float linear, float eased) {
-        if (getEnableLightReveal()) {
+        if (mFeatureFlags.useNewLockscreenAnimations()) {
             mLightRevealScrim.setRevealAmount(1f - linear);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 36519ac..983b296e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -180,8 +180,8 @@
 
     @Override
     public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row,
-            View clickedView) {
-        if (mKeyguardStateController.isShowing()) {
+            View clickedView, boolean deferBouncer, Runnable runnable) {
+        if (!deferBouncer && mKeyguardStateController.isShowing()) {
             onLockedRemoteInput(row, clickedView);
         } else {
             if (row.isChildInGroup() && !row.areChildrenExpanded()) {
@@ -189,7 +189,7 @@
                 mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
             }
             row.setUserExpanded(true);
-            row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick);
+            row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 9e9533d..b572c57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -47,6 +47,7 @@
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -200,7 +201,8 @@
             DismissCallbackRegistry dismissCallbackRegistry,
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
             NotificationIconAreaController notificationIconAreaController,
-            BrightnessSlider.Factory brightnessSliderFactory) {
+            BrightnessSlider.Factory brightnessSliderFactory,
+            FeatureFlags featureFlags) {
         return new StatusBar(
                 context,
                 notificationsController,
@@ -279,6 +281,7 @@
                 notificationShadeDepthController,
                 statusBarTouchableRegionManager,
                 notificationIconAreaController,
-                brightnessSliderFactory);
+                brightnessSliderFactory,
+                featureFlags);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index 08a4492..ccaa1f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -25,6 +25,8 @@
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -42,11 +44,18 @@
     private static final int MSG_MOBILE_DATA_ENABLED_CHANGED = 5;
     private static final int MSG_ADD_REMOVE_EMERGENCY        = 6;
     private static final int MSG_ADD_REMOVE_SIGNAL           = 7;
+    private static final int HISTORY_SIZE = 64;
+    private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
     // All the callbacks.
     private final ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<>();
     private final ArrayList<SignalCallback> mSignalCallbacks = new ArrayList<>();
 
+    // Save the previous HISTORY_SIZE states for logging.
+    private final String[] mHistory = new String[HISTORY_SIZE];
+    // Where to copy the next state into.
+    private int mHistoryIndex;
+
     public CallbackHandler() {
         super(Looper.getMainLooper());
     }
@@ -111,12 +120,27 @@
     public void setWifiIndicators(final boolean enabled, final IconState statusIcon,
             final IconState qsIcon, final boolean activityIn, final boolean activityOut,
             final String description, boolean isTransient, String secondaryLabel) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setWifiIndicators: ")
+                .append("enabled=").append(enabled).append(",")
+                .append("statusIcon=").append(statusIcon).append(",")
+                .append("qsIcon=").append(qsIcon).append(",")
+                .append("activityIn=").append(activityIn).append(",")
+                .append("activityOut=").append(activityOut).append(",")
+                .append("description=").append(description).append(",")
+                .append("isTransient=").append(isTransient).append(",")
+                .append("secondaryLabel=").append(secondaryLabel)
+                .toString();
+        recordLastCallback(log);
         post(() -> {
             for (SignalCallback callback : mSignalCallbacks) {
                 callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut,
                         description, isTransient, secondaryLabel);
             }
         });
+
+
     }
 
     @Override
@@ -125,6 +149,25 @@
             final boolean activityOut, final CharSequence typeContentDescription,
             CharSequence typeContentDescriptionHtml, final CharSequence description,
             final boolean isWide, final int subId, boolean roaming, boolean showTriangle) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setMobileDataIndicators: ")
+                .append("statusIcon=").append(statusIcon).append(",")
+                .append("qsIcon=").append(qsIcon).append(",")
+                .append("statusType=").append(statusType).append(",")
+                .append("qsType=").append(qsType).append(",")
+                .append("activityIn=").append(activityIn).append(",")
+                .append("activityOut=").append(activityOut).append(",")
+                .append("typeContentDescription=").append(typeContentDescription).append(",")
+                .append("typeContentDescriptionHtml=").append(typeContentDescriptionHtml)
+                .append(",")
+                .append("description=").append(description).append(",")
+                .append("isWide=").append(isWide).append(",")
+                .append("subId=").append(subId).append(",")
+                .append("roaming=").append(roaming).append(",")
+                .append("showTriangle=").append(showTriangle)
+                .toString();
+        recordLastCallback(log);
         post(() -> {
             for (SignalCallback signalCluster : mSignalCallbacks) {
                 signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
@@ -138,6 +181,14 @@
     @Override
     public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
                 boolean noNetworksAvailable) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setConnectivityStatus: ")
+                .append("noDefaultNetwork=").append(noDefaultNetwork).append(",")
+                .append("noValidatedNetwork=").append(noValidatedNetwork).append(",")
+                .append("noNetworksAvailable=").append(noNetworksAvailable)
+                .toString();
+        recordLastCallback(log);
         post(() -> {
             for (SignalCallback signalCluster : mSignalCallbacks) {
                 signalCluster.setConnectivityStatus(
@@ -148,6 +199,13 @@
 
     @Override
     public void setNoCallingStatus(boolean noCalling, int subId) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setNoCallingStatus: ")
+                .append("noCalling=").append(noCalling).append(",")
+                .append("subId=").append(subId)
+                .toString();
+        recordLastCallback(log);
         post(() -> {
             for (SignalCallback signalCluster : mSignalCallbacks) {
                 signalCluster.setNoCallingStatus(noCalling, subId);
@@ -157,16 +215,35 @@
 
     @Override
     public void setSubs(List<SubscriptionInfo> subs) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setSubs: ")
+                .append("subs=").append(subs == null ? "" : subs.toString())
+                .toString();
+        recordLastCallback(log);
         obtainMessage(MSG_SUBS_CHANGED, subs).sendToTarget();
     }
 
     @Override
     public void setNoSims(boolean show, boolean simDetected) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setNoSims: ")
+                .append("show=").append(show).append(",")
+                .append("simDetected=").append(simDetected)
+                .toString();
+        recordLastCallback(log);
         obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, simDetected ? 1 : 0).sendToTarget();
     }
 
     @Override
     public void setMobileDataEnabled(boolean enabled) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setMobileDataEnabled: ")
+                .append("enabled=").append(enabled)
+                .toString();
+        recordLastCallback(log);
         obtainMessage(MSG_MOBILE_DATA_ENABLED_CHANGED, enabled ? 1 : 0, 0).sendToTarget();
     }
 
@@ -177,11 +254,23 @@
 
     @Override
     public void setEthernetIndicators(IconState icon) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setEthernetIndicators: ")
+                .append("icon=").append(icon)
+                .toString();
+        recordLastCallback(log);
         obtainMessage(MSG_ETHERNET_CHANGED, icon).sendToTarget();;
     }
 
     @Override
     public void setIsAirplaneMode(IconState icon) {
+        String log = new StringBuilder()
+                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                .append("setIsAirplaneMode: ")
+                .append("icon=").append(icon)
+                .toString();
+        recordLastCallback(log);
         obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget();;
     }
 
@@ -193,4 +282,25 @@
         obtainMessage(MSG_ADD_REMOVE_SIGNAL, listening ? 1 : 0, 0, listener).sendToTarget();
     }
 
+    protected void recordLastCallback(String callback) {
+        mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)] = callback;
+    }
+
+    /**
+     * Dump the Callback logs
+     */
+    public void dump(PrintWriter pw) {
+        pw.println("  - CallbackHandler -----");
+        int size = 0;
+        for (int i = 0; i < HISTORY_SIZE; i++) {
+            if (mHistory[i] != null) size++;
+        }
+        // Print out the previous states in ordered number.
+        for (int i = mHistoryIndex + HISTORY_SIZE - 1;
+                i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
+            pw.println("  Previous Callback(" + (mHistoryIndex + HISTORY_SIZE - i) + "): "
+                    + mHistory[i & (HISTORY_SIZE - 1)]);
+        }
+    }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 39472de..0fe338e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -46,6 +46,7 @@
 import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.mobile.MobileMappings.Config;
 import com.android.settingslib.mobile.MobileStatusTracker;
+import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus;
 import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults;
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.net.SignalStrengthUtil;
@@ -54,6 +55,7 @@
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
 import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.BitSet;
 import java.util.List;
 import java.util.Map;
@@ -62,6 +64,8 @@
  * Monitors the mobile signal changes and update the SysUI icons.
  */
 public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
+    private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+
     private final TelephonyManager mPhone;
     private final SubscriptionDefaults mDefaults;
     private final String mNetworkNameDefault;
@@ -90,6 +94,11 @@
     @VisibleForTesting
     MobileStatusTracker mMobileStatusTracker;
 
+    // Save the previous HISTORY_SIZE states for logging.
+    private final String[] mMobileStatusHistory = new String[HISTORY_SIZE];
+    // Where to copy the next state into.
+    private int mMobileStatusHistoryIndex;
+
     // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
     // need listener lists anymore.
     public MobileSignalController(Context context, Config config, boolean hasMobileData,
@@ -126,12 +135,17 @@
         mCallback = new MobileStatusTracker.Callback() {
             @Override
             public void onMobileStatusChanged(boolean updateTelephony,
-                    MobileStatusTracker.MobileStatus mobileStatus) {
+                    MobileStatus mobileStatus) {
                 if (Log.isLoggable(mTag, Log.DEBUG)) {
                     Log.d(mTag, "onMobileStatusChanged="
                             + " updateTelephony=" + updateTelephony
                             + " mobileStatus=" + mobileStatus.toString());
                 }
+                String status = new StringBuilder()
+                        .append(SSDF.format(System.currentTimeMillis())).append(",")
+                        .append(mobileStatus.toString())
+                        .toString();
+                recordLastMobileStatus(status);
                 updateMobileStatus(mobileStatus);
                 if (updateTelephony) {
                     updateTelephony();
@@ -343,16 +357,8 @@
         return Utils.isInService(mServiceState);
     }
 
-    String getNonDefaultCarrierName() {
-        if (!mCurrentState.networkNameData.equals(mNetworkNameDefault)) {
-            return mCurrentState.networkNameData;
-        } else if (mSubscriptionInfo.getCarrierName() != null) {
-            return mSubscriptionInfo.getCarrierName().toString();
-        } else if (mSubscriptionInfo.getDisplayName() != null) {
-            return mSubscriptionInfo.getDisplayName().toString();
-        } else {
-            return "";
-        }
+    String getNetworkNameForCarrierWiFi() {
+        return mPhone.getSimOperatorName();
     }
 
     private boolean isRoaming() {
@@ -455,7 +461,7 @@
         return CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
-    private void updateMobileStatus(MobileStatusTracker.MobileStatus mobileStatus) {
+    private void updateMobileStatus(MobileStatus mobileStatus) {
         mCurrentState.activityIn = mobileStatus.activityIn;
         mCurrentState.activityOut = mobileStatus.activityOut;
         mCurrentState.dataSim = mobileStatus.dataSim;
@@ -570,6 +576,10 @@
         notifyListenersIfNecessary();
     }
 
+    private void recordLastMobileStatus(String mobileStatus) {
+        mMobileStatusHistory[mMobileStatusHistoryIndex++ & (HISTORY_SIZE - 1)] = mobileStatus;
+    }
+
     @Override
     public void dump(PrintWriter pw) {
         super.dump(pw);
@@ -580,5 +590,17 @@
         pw.println("  mDataState=" + mDataState + ",");
         pw.println("  mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
         pw.println("  isDataDisabled=" + isDataDisabled() + ",");
+        pw.println("  MobileStatusHistory");
+        int size = 0;
+        for (int i = 0; i < HISTORY_SIZE; i++) {
+            if (mMobileStatusHistory[i] != null) size++;
+        }
+        // Print out the previous states in ordered number.
+        for (int i = mMobileStatusHistoryIndex + HISTORY_SIZE - 1;
+                i >= mMobileStatusHistoryIndex + HISTORY_SIZE - size; i--) {
+            pw.println("  Previous MobileStatus("
+                    + (mMobileStatusHistoryIndex + HISTORY_SIZE - i) + "): "
+                    + mMobileStatusHistory[i & (HISTORY_SIZE - 1)]);
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index e13e30b..80c7811 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -76,6 +76,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.Collections;
@@ -100,6 +101,8 @@
     private static final int EMERGENCY_VOICE_CONTROLLER = 200;
     private static final int EMERGENCY_NO_SUB = 300;
     private static final int EMERGENCY_ASSUMED_VOICE_CONTROLLER = 400;
+    private static final int HISTORY_SIZE = 16;
+    private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
     private final Context mContext;
     private final TelephonyManager mPhone;
@@ -150,6 +153,11 @@
     // This list holds our ordering.
     private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>();
 
+    // Save the previous HISTORY_SIZE states for logging.
+    private final String[] mHistory = new String[HISTORY_SIZE];
+    // Where to copy the next state into.
+    private int mHistoryIndex;
+
     @VisibleForTesting
     boolean mListening;
 
@@ -307,6 +315,12 @@
             public void onLost(Network network) {
                 mLastNetwork = null;
                 mLastNetworkCapabilities = null;
+                String callback = new StringBuilder()
+                        .append(SSDF.format(System.currentTimeMillis())).append(",")
+                        .append("onLost: ")
+                        .append("network=").append(network)
+                        .toString();
+                recordLastNetworkCallback(callback);
                 updateConnectivity();
             }
 
@@ -327,6 +341,13 @@
                 }
                 mLastNetwork = network;
                 mLastNetworkCapabilities = networkCapabilities;
+                String callback = new StringBuilder()
+                        .append(SSDF.format(System.currentTimeMillis())).append(",")
+                        .append("onCapabilitiesChanged: ")
+                        .append("network=").append(network).append(",")
+                        .append("networkCapabilities=").append(networkCapabilities)
+                        .toString();
+                recordLastNetworkCallback(callback);
                 updateConnectivity();
             }
         };
@@ -528,9 +549,9 @@
         return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
     }
 
-    String getNonDefaultMobileDataNetworkName(int subId) {
+    String getNetworkNameForCarrierWiFi(int subId) {
         MobileSignalController controller = getControllerWithSubId(subId);
-        return controller != null ? controller.getNonDefaultCarrierName() : "";
+        return controller != null ? controller.getNetworkNameForCarrierWiFi() : "";
     }
 
     private void notifyControllersMobileDataChanged() {
@@ -996,6 +1017,19 @@
         pw.print("  mEmergencySource=");
         pw.println(emergencyToString(mEmergencySource));
 
+        pw.println("  - DefaultNetworkCallback -----");
+        int size = 0;
+        for (int i = 0; i < HISTORY_SIZE; i++) {
+            if (mHistory[i] != null) {
+                size++;
+            }
+        }
+        for (int i = mHistoryIndex + HISTORY_SIZE - 1;
+                i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
+            pw.println("  Previous NetworkCallback(" + (mHistoryIndex + HISTORY_SIZE - i) + "): "
+                    + mHistory[i & (HISTORY_SIZE - 1)]);
+        }
+
         pw.println("  - config ------");
         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
@@ -1006,6 +1040,8 @@
         mEthernetSignalController.dump(pw);
 
         mAccessPoints.dump(pw);
+
+        mCallbackHandler.dump(pw);
     }
 
     private static final String emergencyToString(int emergencySource) {
@@ -1235,6 +1271,10 @@
         return s;
     }
 
+    private void recordLastNetworkCallback(String callback) {
+        mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)] = callback;
+    }
+
     private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
         SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
                 null, null, null, "", false, null, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 9380d91..8e833c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -73,6 +73,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo;
@@ -80,6 +81,7 @@
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.LightBarController;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
@@ -99,6 +101,8 @@
 
     private final SendButtonTextWatcher mTextWatcher;
     private final TextView.OnEditorActionListener mEditorActionHandler;
+    private final NotificationRemoteInputManager mRemoteInputManager;
+    private final List<OnFocusChangeListener> mEditTextFocusChangeListeners = new ArrayList<>();
     private RemoteEditText mEditText;
     private ImageButton mSendButton;
     private ProgressBar mProgressBar;
@@ -121,12 +125,14 @@
     private boolean mResetting;
     private NotificationViewWrapper mWrapper;
     private Consumer<Boolean> mOnVisibilityChangedListener;
+    private NotificationRemoteInputManager.BouncerChecker mBouncerChecker;
 
     public RemoteInputView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mTextWatcher = new SendButtonTextWatcher();
         mEditorActionHandler = new EditorActionHandler();
         mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class);
+        mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
         mStatusBarManagerService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
     }
@@ -200,6 +206,11 @@
     }
 
     private void sendRemoteInput(Intent intent) {
+        if (mBouncerChecker != null && mBouncerChecker.showBouncerIfNecessary()) {
+            mEditText.hideIme();
+            return;
+        }
+
         mEditText.setEnabled(false);
         mSendButton.setVisibility(INVISIBLE);
         mProgressBar.setVisibility(VISIBLE);
@@ -351,6 +362,11 @@
         }
     }
 
+    /** Populates the text field of the remote input with the given content. */
+    public void setEditTextContent(@Nullable CharSequence editTextContent) {
+        mEditText.setText(editTextContent);
+    }
+
     public void focusAnimated() {
         if (getVisibility() != VISIBLE) {
             Animator animator = ViewAnimationUtils.createCircularReveal(
@@ -552,6 +568,37 @@
         return getVisibility() == VISIBLE && mController.isSpinning(mEntry.getKey(), mToken);
     }
 
+    /**
+     * Sets a {@link com.android.systemui.statusbar.NotificationRemoteInputManager.BouncerChecker}
+     * that will be used to determine if the device needs to be unlocked before sending the
+     * RemoteInput.
+     */
+    public void setBouncerChecker(
+            @Nullable NotificationRemoteInputManager.BouncerChecker bouncerChecker) {
+        mBouncerChecker = bouncerChecker;
+    }
+
+    /** Registers a listener for focus-change events on the EditText */
+    public void addOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) {
+        mEditTextFocusChangeListeners.add(listener);
+    }
+
+    /** Removes a previously-added listener for focus-change events on the EditText */
+    public void removeOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) {
+        mEditTextFocusChangeListeners.remove(listener);
+    }
+
+    /** Determines if the EditText has focus. */
+    public boolean editTextHasFocus() {
+        return mEditText != null && mEditText.hasFocus();
+    }
+
+    private void onEditTextFocusChanged(RemoteEditText remoteEditText, boolean focused) {
+        for (View.OnFocusChangeListener listener : mEditTextFocusChangeListeners) {
+            listener.onFocusChange(remoteEditText, focused);
+        }
+    }
+
     /** Handler for button click on send action in IME. */
     private class EditorActionHandler implements TextView.OnEditorActionListener {
 
@@ -603,6 +650,7 @@
         private RemoteInputView mRemoteInputView;
         boolean mShowImeOnInputConnection;
         private LightBarController mLightBarController;
+        private InputMethodManager mInputMethodManager;
         UserHandle mUser;
 
         public RemoteEditText(Context context, AttributeSet attrs) {
@@ -621,6 +669,12 @@
             setOnReceiveContentListener(types, listener);
         }
 
+        private void hideIme() {
+            if (mInputMethodManager != null) {
+                mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+            }
+        }
+
         private void defocusIfNeeded(boolean animate) {
             if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition()
                     || isTemporarilyDetached()) {
@@ -654,6 +708,9 @@
         @Override
         protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
             super.onFocusChanged(focused, direction, previouslyFocusedRect);
+            if (mRemoteInputView != null) {
+                mRemoteInputView.onEditTextFocusChanged(this, focused);
+            }
             if (!focused) {
                 defocusIfNeeded(true /* animate */);
             }
@@ -724,17 +781,16 @@
 
             if (mShowImeOnInputConnection && ic != null) {
                 Context targetContext = userContext != null ? userContext : getContext();
-                final InputMethodManager imm =
-                        targetContext.getSystemService(InputMethodManager.class);
-                if (imm != null) {
+                mInputMethodManager = targetContext.getSystemService(InputMethodManager.class);
+                if (mInputMethodManager != null) {
                     // onCreateInputConnection is called by InputMethodManager in the middle of
                     // setting up the connection to the IME; wait with requesting the IME until that
                     // work has completed.
                     post(new Runnable() {
                         @Override
                         public void run() {
-                            imm.viewClicked(RemoteEditText.this);
-                            imm.showSoftInput(RemoteEditText.this, 0);
+                            mInputMethodManager.viewClicked(RemoteEditText.this);
+                            mInputMethodManager.showSoftInput(RemoteEditText.this, 0);
                         }
                     });
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index b2120d4..1fd2ccb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -147,7 +147,7 @@
         IconState qsIcon = new IconState(
                 mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(), contentDescription);
         CharSequence description =
-                mNetworkController.getNonDefaultMobileDataNetworkName(mCurrentState.subId);
+                mNetworkController.getNetworkNameForCarrierWiFi(mCurrentState.subId);
         callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                 mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription,
                 dataContentDescriptionHtml, description, icons.isWide,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index b7aa907..b42dde6 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -75,6 +75,7 @@
 import com.android.wm.shell.sizecompatui.SizeCompatUIController;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.transition.RemoteTransitions;
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.Optional;
@@ -179,7 +180,7 @@
             DragAndDropController dragAndDropController,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<LegacySplitScreen> legacySplitScreenOptional,
-            Optional<SplitScreen> splitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             Optional<AppPairs> appPairsOptional,
             FullscreenTaskListener fullscreenTaskListener,
             Transitions transitions,
@@ -204,7 +205,7 @@
     static Optional<ShellCommandHandler> provideShellCommandHandler(
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<LegacySplitScreen> legacySplitScreenOptional,
-            Optional<SplitScreen> splitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             Optional<Pip> pipOptional,
             Optional<OneHanded> oneHandedOptional,
             Optional<HideDisplayCutout> hideDisplayCutout,
@@ -319,12 +320,21 @@
 
     @WMSingleton
     @Provides
-    static Optional<SplitScreen> provideSplitScreen(ShellTaskOrganizer shellTaskOrganizer,
+    static Optional<SplitScreen> provideSplitScreen(
+            Optional<SplitScreenController> splitScreenController) {
+        return splitScreenController.map((controller) -> controller.asSplitScreen());
+    }
+
+    @WMSingleton
+    @Provides
+    static Optional<SplitScreenController> provideSplitScreenController(
+            ShellTaskOrganizer shellTaskOrganizer,
             SyncTransactionQueue syncQueue, Context context,
-            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+            @ShellMainThread ShellExecutor mainExecutor) {
         if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
             return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
-                    rootTaskDisplayAreaOrganizer));
+                    rootTaskDisplayAreaOrganizer, mainExecutor));
         } else {
             return Optional.empty();
         }
@@ -388,6 +398,12 @@
 
     @WMSingleton
     @Provides
+    static RemoteTransitions provideRemoteTransitions(Transitions transitions) {
+        return Transitions.asRemoteTransitions(transitions);
+    }
+
+    @WMSingleton
+    @Provides
     static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellAnimationThread ShellExecutor animExecutor) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 78af20f..ffd747e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -75,6 +75,7 @@
         mFakeSettings = new FakeSettings();
 
         mTile = new ReduceBrightColorsTile(
+                true,
                 mHost,
                 mTestableLooper.getLooper(),
                 new Handler(mTestableLooper.getLooper()),
@@ -95,7 +96,6 @@
         assertEquals(Tile.STATE_INACTIVE, mTile.getState().state);
         assertEquals(mTile.getState().label.toString(),
                 mContext.getString(R.string.quick_settings_reduce_bright_colors_label));
-        assertEquals(mTile.getState().secondaryLabel.toString(), "");
     }
 
     @Test
@@ -128,13 +128,5 @@
         assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
         assertEquals(mTile.getState().label.toString(),
                 mContext.getString(R.string.quick_settings_reduce_bright_colors_label));
-
-        final int intensity = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mUserTracker.getUserId());
-
-        assertEquals(
-                mContext.getString(
-                        R.string.quick_settings_reduce_bright_colors_secondary_label, intensity),
-                mTile.getState().secondaryLabel.toString());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
index 451c78f..6d2b8e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
@@ -43,7 +43,7 @@
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -78,7 +78,7 @@
     @Mock private Optional<com.android.wm.shell.onehanded.OneHanded> mMockOneHandedOptional;
     @Mock private PackageManager mPackageManager;
     @Mock private SysUiState mMockSysUiState;
-    @Mock private Transitions mMockTransitions;
+    @Mock private RemoteTransitions mMockTransitions;
 
     @Before
     public void setUp() throws RemoteException {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index fa253e6..b9fd75e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -35,6 +35,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.tuner.TunerService;
 
@@ -57,6 +58,7 @@
     @Mock private PowerManager mPowerManager;
     @Mock private TunerService mTunerService;
     @Mock private BatteryController mBatteryController;
+    @Mock private FeatureFlags mFeatureFlags;
 
     @Before
     public void setup() {
@@ -67,7 +69,8 @@
             mAlwaysOnDisplayPolicy,
             mPowerManager,
             mBatteryController,
-            mTunerService
+            mTunerService,
+            mFeatureFlags
         );
     }
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index cae488a..253460d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -97,6 +97,7 @@
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -258,6 +259,7 @@
     @Mock private DemoModeController mDemoModeController;
     @Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
     @Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
+    @Mock private FeatureFlags mFeatureFlags;
     private ShadeController mShadeController;
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
     private InitController mInitController = new InitController();
@@ -418,7 +420,8 @@
                 mNotificationShadeDepthControllerLazy,
                 mStatusBarTouchableRegionManager,
                 mNotificationIconAreaController,
-                mBrightnessSliderFactory);
+                mBrightnessSliderFactory,
+                mFeatureFlags);
 
         when(mNotificationShadeWindowView.findViewById(R.id.lock_icon_container)).thenReturn(
                 mLockIconContainer);
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
index 4d95ef1..6dcad25 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
@@ -20,14 +20,11 @@
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
 
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
 import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.SpannableStringBuilder;
@@ -45,7 +42,7 @@
 
     private static final String TAG = "VpnDisconnected";
 
-    private IConnectivityManager mService;
+    private ConnectivityManager mService;
     private int mUserId;
     private String mVpnPackage;
 
@@ -53,10 +50,9 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mService = IConnectivityManager.Stub.asInterface(
-                ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
         mUserId = UserHandle.myUserId();
-        mVpnPackage = getAlwaysOnVpnPackage();
+        final ConnectivityManager cm = getSystemService(ConnectivityManager.class);
+        mVpnPackage = cm.getAlwaysOnVpnPackageForUser(mUserId);
         if (mVpnPackage == null) {
             finish();
             return;
@@ -102,15 +98,6 @@
         }
     }
 
-    private String getAlwaysOnVpnPackage() {
-        try {
-            return mService.getAlwaysOnVpnPackage(mUserId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Can't getAlwaysOnVpnPackage()", e);
-            return null;
-        }
-    }
-
     private CharSequence getVpnLabel() {
         try {
             return VpnConfig.getVpnLabel(this, mVpnPackage);
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index e66f2cc..aab01d0 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -18,15 +18,12 @@
 
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
 import android.net.VpnManager;
 import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.Html;
@@ -48,7 +45,8 @@
 
     private String mPackage;
 
-    private IConnectivityManager mService;
+    private ConnectivityManager mCm;  // TODO: switch entirely to VpnManager once VPN code moves
+    private VpnManager mVm;
 
     public ConfirmDialog() {
         this(VpnManager.TYPE_VPN_SERVICE);
@@ -62,10 +60,10 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mPackage = getCallingPackage();
-        mService = IConnectivityManager.Stub.asInterface(
-                ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+        mCm = getSystemService(ConnectivityManager.class);
+        mVm = getSystemService(VpnManager.class);
 
-        if (prepareVpn()) {
+        if (mVm.prepareVpn(mPackage, null, UserHandle.myUserId())) {
             setResult(RESULT_OK);
             finish();
             return;
@@ -74,7 +72,7 @@
             finish();
             return;
         }
-        final String alwaysOnVpnPackage = getAlwaysOnVpnPackage();
+        final String alwaysOnVpnPackage = mCm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId());
         // Can't prepare new vpn app when another vpn is always-on
         if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) {
             finish();
@@ -97,24 +95,6 @@
         button.setFilterTouchesWhenObscured(true);
     }
 
-    private String getAlwaysOnVpnPackage() {
-        try {
-           return mService.getAlwaysOnVpnPackage(UserHandle.myUserId());
-        } catch (RemoteException e) {
-            Log.e(TAG, "fail to call getAlwaysOnVpnPackage", e);
-            // Fallback to null to show the dialog
-            return null;
-        }
-    }
-
-    private boolean prepareVpn() {
-        try {
-            return mService.prepareVpn(mPackage, null, UserHandle.myUserId());
-        } catch (RemoteException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
     private CharSequence getVpnLabel() {
         try {
             return VpnConfig.getVpnLabel(this, mPackage);
@@ -146,10 +126,10 @@
     @Override
     public void onClick(DialogInterface dialog, int which) {
         try {
-            if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) {
+            if (mVm.prepareVpn(null, mPackage, UserHandle.myUserId())) {
                 // Authorize this app to initiate VPN connections in the future without user
                 // intervention.
-                mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
+                mVm.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
                 setResult(RESULT_OK);
             }
         } catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index 01dca7e..1fc74f7 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -16,13 +16,11 @@
 
 package com.android.vpndialogs;
 
-import android.content.Context;
 import android.content.DialogInterface;
-import android.net.IConnectivityManager;
+import android.net.VpnManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Log;
@@ -41,7 +39,7 @@
 
     private VpnConfig mConfig;
 
-    private IConnectivityManager mService;
+    private VpnManager mVm;
 
     private TextView mDuration;
     private TextView mDataTransmitted;
@@ -55,11 +53,9 @@
         super.onCreate(savedInstanceState);
 
         try {
+            mVm = getSystemService(VpnManager.class);
 
-            mService = IConnectivityManager.Stub.asInterface(
-                    ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-
-            mConfig = mService.getVpnConfig(UserHandle.myUserId());
+            mConfig = mVm.getVpnConfig(UserHandle.myUserId());
 
             // mConfig can be null if we are a restricted user, in that case don't show this dialog
             if (mConfig == null) {
@@ -118,9 +114,9 @@
             } else if (which == DialogInterface.BUTTON_NEUTRAL) {
                 final int myUserId = UserHandle.myUserId();
                 if (mConfig.legacy) {
-                    mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
+                    mVm.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
                 } else {
-                    mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
+                    mVm.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
                 }
             }
         } catch (Exception e) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 2626654..a9e8ea0 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -372,12 +372,11 @@
 
     @Override
     public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
-        final boolean isTouchableDisplay = mWindowManagerService.isTouchableDisplay(displayId);
         synchronized (mLock) {
             if (mSecurityPolicy.canPerformGestures(this)) {
                 MotionEventInjector motionEventInjector =
                         mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId);
-                if (motionEventInjector != null && isTouchableDisplay) {
+                if (mWindowManagerService.isTouchOrFaketouchDevice()) {
                     motionEventInjector.injectEvents(
                             gestureSteps.getList(), mServiceInterface, sequence, displayId);
                 } else {
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index e4a86c3..eff410c 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2647,6 +2647,8 @@
             info.icon = activityInfo.getIconResource();
             info.previewImage = sa.getResourceId(
                     com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
+            info.previewLayout = sa.getResourceId(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_previewLayout, 0);
             info.autoAdvanceViewId = sa.getResourceId(
                     com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
             info.resizeMode = sa.getInt(
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 55e3ef2..96b69dc 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -17,9 +17,17 @@
 
 package com.android.server.companion;
 
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
+import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
+import static android.content.Context.BIND_IMPORTANT;
+import static android.content.pm.PackageManager.MATCH_ALL;
+
+import static com.android.internal.util.CollectionUtils.any;
 import static com.android.internal.util.CollectionUtils.emptyIfNull;
+import static com.android.internal.util.CollectionUtils.filter;
 import static com.android.internal.util.CollectionUtils.find;
 import static com.android.internal.util.CollectionUtils.forEach;
+import static com.android.internal.util.CollectionUtils.map;
 import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkNotNull;
@@ -40,22 +48,32 @@
 import android.app.role.RoleManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
 import android.companion.Association;
 import android.companion.AssociationRequest;
 import android.companion.CompanionDeviceManager;
+import android.companion.CompanionDeviceService;
 import android.companion.DeviceNotAssociatedException;
 import android.companion.ICompanionDeviceDiscoveryService;
 import android.companion.ICompanionDeviceManager;
+import android.companion.ICompanionDeviceService;
 import android.companion.IFindDeviceCallback;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.net.NetworkPolicyManager;
 import android.os.Binder;
@@ -77,6 +95,7 @@
 import android.provider.Settings;
 import android.provider.SettingsStringUtil.ComponentNameSet;
 import android.text.BidiFormatter;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.ExceptionUtils;
@@ -115,6 +134,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -135,6 +155,9 @@
             CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
             ".DeviceDiscoveryService");
 
+    private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000;
+    private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000;
+
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "CompanionDeviceManagerService";
 
@@ -146,18 +169,23 @@
     private static final String XML_ATTR_PACKAGE = "package";
     private static final String XML_ATTR_DEVICE = "device";
     private static final String XML_ATTR_PROFILE = "profile";
-    private static final String XML_ATTR_PERSISTENT_PROFILE_GRANTS = "persistent_profile_grants";
+    private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby";
     private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
 
     private final CompanionDeviceManagerImpl mImpl;
     private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
     private PowerWhitelistManager mPowerWhitelistManager;
     private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
+    /** userId -> packageName -> serviceConnector */
+    private PerUser<ArrayMap<String, ServiceConnector<ICompanionDeviceService>>>
+            mDeviceListenerServiceConnectors;
     private IAppOpsService mAppOpsManager;
     private RoleManager mRoleManager;
     private BluetoothAdapter mBluetoothAdapter;
+    private UserManager mUserManager;
 
     private IFindDeviceCallback mFindDeviceCallback;
+    private ScanCallback mBleScanCallback = new BleScanCallback();
     private AssociationRequest mRequest;
     private String mCallingPackage;
     private AndroidFuture<Association> mOngoingDeviceDiscovery;
@@ -165,9 +193,16 @@
 
     private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener =
             new BluetoothDeviceConnectedListener();
+    private BleStateBroadcastReceiver mBleStateBroadcastReceiver = new BleStateBroadcastReceiver();
     private List<String> mCurrentlyConnectedDevices = new ArrayList<>();
+    private ArrayMap<String, Date> mDevicesLastNearby = new ArrayMap<>();
+    private UnbindDeviceListenersRunnable
+            mUnbindDeviceListenersRunnable = new UnbindDeviceListenersRunnable();
+    private ArrayMap<String, TriggerDeviceDisappearedRunnable> mTriggerDeviceDisappearedRunnables =
+            new ArrayMap<>();
 
     private final Object mLock = new Object();
+    private final Handler mMainHandler = Handler.getMain();
 
     /** userId -> [association] */
     @GuardedBy("mLock")
@@ -189,6 +224,7 @@
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mPermissionControllerManager = requireNonNull(
                 context.getSystemService(PermissionControllerManager.class));
+        mUserManager = context.getSystemService(UserManager.class);
 
         Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
         mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
@@ -201,6 +237,16 @@
             }
         };
 
+        mDeviceListenerServiceConnectors = new PerUser<ArrayMap<String,
+                ServiceConnector<ICompanionDeviceService>>>() {
+            @NonNull
+            @Override
+            protected ArrayMap<String, ServiceConnector<ICompanionDeviceService>> create(
+                    int userId) {
+                return new ArrayMap<>();
+            }
+        };
+
         registerPackageMonitor();
     }
 
@@ -208,10 +254,13 @@
         new PackageMonitor() {
             @Override
             public void onPackageRemoved(String packageName, int uid) {
+                int userId = getChangingUserId();
                 updateAssociations(
                         as -> CollectionUtils.filter(as,
                                 a -> !Objects.equals(a.getPackageName(), packageName)),
-                        getChangingUserId());
+                        userId);
+
+                unbindDevicePresenceListener(packageName, userId);
             }
 
             @Override
@@ -225,6 +274,15 @@
         }.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
     }
 
+    private void unbindDevicePresenceListener(String packageName, int userId) {
+        ServiceConnector<ICompanionDeviceService> deviceListener =
+                mDeviceListenerServiceConnectors.forUser(userId)
+                        .remove(packageName);
+        if (deviceListener != null) {
+            deviceListener.unbind();
+        }
+    }
+
     @Override
     public void onStart() {
         publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl);
@@ -233,11 +291,17 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            // Init Bluetooth
             mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
             if (mBluetoothAdapter != null) {
                 mBluetoothAdapter.registerBluetoothConnectionCallback(
                         getContext().getMainExecutor(),
                         mBluetoothDeviceConnectedListener);
+                getContext().registerReceiver(
+                        mBleStateBroadcastReceiver, mBleStateBroadcastReceiver.mIntentFilter);
+                initBleScanning();
+            } else {
+                Log.w(LOG_TAG, "No BluetoothAdapter available");
             }
         }
     }
@@ -287,7 +351,7 @@
 
     @Override
     public void binderDied() {
-        Handler.getMain().post(this::cleanup);
+        mMainHandler.post(this::cleanup);
     }
 
     private void cleanup() {
@@ -399,7 +463,7 @@
                 checkCallerIsSystemOr(callingPackage, userId);
                 checkUsesFeature(callingPackage, getCallingUserId());
             }
-            return new ArrayList<>(CollectionUtils.map(
+            return new ArrayList<>(map(
                     getAllAssociations(userId, callingPackage),
                     a -> a.getDeviceMacAddress()));
         }
@@ -497,7 +561,7 @@
                 return true;
             }
 
-            return CollectionUtils.any(
+            return any(
                     getAllAssociations(userId, packageName),
                     a -> Objects.equals(a.getDeviceMacAddress(), macAddress));
         }
@@ -506,22 +570,18 @@
         public void registerDevicePresenceListenerService(
                 String packageName, String deviceAddress)
                 throws RemoteException {
-            checkCanRegisterObserverService(packageName, deviceAddress);
-
-            //TODO(eugenesusla) implement
+            registerDevicePresenceListenerActive(packageName, deviceAddress, true);
         }
 
         @Override
         public void unregisterDevicePresenceListenerService(
                 String packageName, String deviceAddress)
                 throws RemoteException {
-            checkCanRegisterObserverService(packageName, deviceAddress);
-
-            //TODO(eugenesusla) implement
+            registerDevicePresenceListenerActive(packageName, deviceAddress, false);
         }
 
-        private void checkCanRegisterObserverService(String packageName, String deviceAddress)
-                throws RemoteException {
+        private void registerDevicePresenceListenerActive(String packageName, String deviceAddress,
+                boolean active) throws RemoteException {
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE,
                     "[un]registerDevicePresenceListenerService");
@@ -537,6 +597,20 @@
                         + " is not associated with device " + deviceAddress
                         + " for user " + userId));
             }
+
+            updateAssociations(associations -> map(associations, association -> {
+                if (Objects.equals(association.getPackageName(), packageName)
+                        && Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
+                    return new Association(
+                            association.getUserId(),
+                            association.getDeviceMacAddress(),
+                            association.getPackageName(),
+                            association.getDeviceProfile(),
+                            active /* notifyOnDeviceNearby */);
+                } else {
+                    return association;
+                }
+            }));
         }
 
         private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
@@ -693,6 +767,10 @@
         if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) {
             grantDeviceProfile(association);
         }
+
+        if (association.isNotifyOnDeviceNearby()) {
+            restartBleScan();
+        }
     }
 
     private void exemptFromAutoRevoke(String packageName, int uid) {
@@ -795,9 +873,9 @@
                                         association.getDeviceMacAddress());
                         if (association.getDeviceProfile() != null) {
                             tag.attribute(null, XML_ATTR_PROFILE, association.getDeviceProfile());
-                            tag.attribute(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS,
+                            tag.attribute(null, XML_ATTR_NOTIFY_DEVICE_NEARBY,
                                     Boolean.toString(
-                                            association.isKeepProfilePrivilegesWhenDeviceAway()));
+                                            association.isNotifyOnDeviceNearby()));
                         }
                         tag.endTag(null, XML_TAG_ASSOCIATION);
                     });
@@ -835,7 +913,12 @@
     }
 
     private List<UserInfo> getAllUsers() {
-        return getContext().getSystemService(UserManager.class).getUsers();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            return mUserManager.getUsers();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     @Nullable
@@ -845,6 +928,19 @@
                 a -> Objects.equals(packageFilter, a.getPackageName()));
     }
 
+    private Set<Association> getAllAssociations() {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            ArraySet<Association> result = new ArraySet<>();
+            for (UserInfo user : mUserManager.getAliveUsers()) {
+                result.addAll(getAllAssociations(user.id));
+            }
+            return result;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     private Set<Association> readAllAssociations(int userId) {
         final AtomicFile file = getStorageFileForUser(userId);
 
@@ -865,7 +961,7 @@
 
                     final String profile = parser.getAttributeValue(null, XML_ATTR_PROFILE);
                     final boolean persistentGrants = Boolean.valueOf(
-                            parser.getAttributeValue(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS));
+                            parser.getAttributeValue(null, XML_ATTR_NOTIFY_DEVICE_NEARBY));
 
                     if (appPackage == null || deviceAddress == null) continue;
 
@@ -896,6 +992,8 @@
                 }
             }
         }
+
+        onDeviceNearby(address);
     }
 
     private void grantDeviceProfile(Association association) {
@@ -919,6 +1017,267 @@
 
     void onDeviceDisconnected(String address) {
         mCurrentlyConnectedDevices.remove(address);
+
+        onDeviceDisappeared(address);
+    }
+
+    private ServiceConnector<ICompanionDeviceService> getDeviceListenerServiceConnector(
+            Association a) {
+        return mDeviceListenerServiceConnectors.forUser(a.getUserId()).computeIfAbsent(
+                a.getPackageName(),
+                pkg -> createDeviceListenerServiceConnector(a));
+    }
+
+    private ServiceConnector<ICompanionDeviceService> createDeviceListenerServiceConnector(
+            Association a) {
+        List<ResolveInfo> resolveInfos = getContext().getPackageManager().queryIntentServicesAsUser(
+                new Intent(CompanionDeviceService.SERVICE_INTERFACE), MATCH_ALL, a.getUserId());
+        List<ResolveInfo> packageResolveInfos = filter(resolveInfos,
+                info -> Objects.equals(info.serviceInfo.packageName, a.getPackageName()));
+        if (packageResolveInfos.size() != 1) {
+            Log.w(LOG_TAG, "Device presence listener package must have exactly one "
+                    + "CompanionDeviceService, but " + a.getPackageName()
+                    + " has " + packageResolveInfos.size());
+            return new ServiceConnector.NoOp<>();
+        }
+        ComponentName componentName = packageResolveInfos.get(0).serviceInfo.getComponentName();
+        Log.i(LOG_TAG, "Initializing CompanionDeviceService binding for " + componentName);
+        return new ServiceConnector.Impl<>(getContext(),
+                new Intent(CompanionDeviceService.SERVICE_INTERFACE).setComponent(componentName),
+                BIND_IMPORTANT,
+                a.getUserId(),
+                ICompanionDeviceService.Stub::asInterface);
+    }
+
+    private class BleScanCallback extends ScanCallback {
+        @Override
+        public void onScanResult(int callbackType, ScanResult result) {
+            if (DEBUG) {
+                Log.i(LOG_TAG, "onScanResult(callbackType = "
+                        + callbackType + ", result = " + result + ")");
+            }
+
+            onDeviceNearby(result.getDevice().getAddress());
+        }
+
+        @Override
+        public void onBatchScanResults(List<ScanResult> results) {
+            for (int i = 0, size = results.size(); i < size; i++) {
+                onScanResult(CALLBACK_TYPE_ALL_MATCHES, results.get(i));
+            }
+        }
+
+        @Override
+        public void onScanFailed(int errorCode) {
+            if (errorCode == SCAN_FAILED_ALREADY_STARTED) {
+                // ignore - this might happen if BT tries to auto-restore scans for us in the
+                // future
+                Log.i(LOG_TAG, "Ignoring BLE scan error: SCAN_FAILED_ALREADY_STARTED");
+            } else {
+                Log.w(LOG_TAG, "Failed to start BLE scan: error " + errorCode);
+            }
+        }
+    }
+
+    private class BleStateBroadcastReceiver extends BroadcastReceiver {
+
+        final IntentFilter mIntentFilter =
+                new IntentFilter(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1);
+            int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+            Log.i(LOG_TAG, "Received BT state transition broadcast: "
+                    + BluetoothAdapter.nameForState(previousState)
+                    + " -> " + BluetoothAdapter.nameForState(newState));
+
+            boolean bleOn = newState == BluetoothAdapter.STATE_ON
+                    || newState == BluetoothAdapter.STATE_BLE_ON;
+            if (bleOn) {
+                if (mBluetoothAdapter.getBluetoothLeScanner() != null) {
+                    startBleScan();
+                } else {
+                    Log.wtf(LOG_TAG, "BLE on, but BluetoothLeScanner == null");
+                }
+            }
+        }
+    }
+
+    private class UnbindDeviceListenersRunnable implements Runnable {
+
+        public String getJobId(String address) {
+            return "CDM_deviceGone_unbind_" + address;
+        }
+
+        @Override
+        public void run() {
+            int size = mDevicesLastNearby.size();
+            for (int i = 0; i < size; i++) {
+                String address = mDevicesLastNearby.keyAt(i);
+                Date lastNearby = mDevicesLastNearby.valueAt(i);
+
+                if (System.currentTimeMillis() - lastNearby.getTime()
+                        >= DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS) {
+                    for (Association association : getAllAssociations(address)) {
+                        if (association.isNotifyOnDeviceNearby()) {
+                            getDeviceListenerServiceConnector(association).unbind();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private class TriggerDeviceDisappearedRunnable implements Runnable {
+
+        private final String mAddress;
+
+        TriggerDeviceDisappearedRunnable(String address) {
+            mAddress = address;
+        }
+
+        public void schedule() {
+            mMainHandler.removeCallbacks(this);
+            mMainHandler.postDelayed(this, this, DEVICE_DISAPPEARED_TIMEOUT_MS);
+        }
+
+        @Override
+        public void run() {
+            onDeviceDisappeared(mAddress);
+        }
+    }
+
+    private Set<Association> getAllAssociations(String deviceAddress) {
+        List<UserInfo> aliveUsers = mUserManager.getAliveUsers();
+        Set<Association> result = new ArraySet<>();
+        for (int i = 0, size = aliveUsers.size(); i < size; i++) {
+            UserInfo user = aliveUsers.get(i);
+            for (Association association : getAllAssociations(user.id)) {
+                if (Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
+                    result.add(association);
+                }
+            }
+        }
+        return result;
+    }
+
+    private void onDeviceNearby(String address) {
+        Date timestamp = new Date();
+        Date oldTimestamp = mDevicesLastNearby.put(address, timestamp);
+
+        cancelUnbindDeviceListener(address);
+
+        mTriggerDeviceDisappearedRunnables
+                .computeIfAbsent(address, addr -> new TriggerDeviceDisappearedRunnable(address))
+                .schedule();
+
+        // Avoid spamming the app if device is already known to be nearby
+        boolean justAppeared = oldTimestamp == null
+                || timestamp.getTime() - oldTimestamp.getTime() >= DEVICE_DISAPPEARED_TIMEOUT_MS;
+        if (justAppeared) {
+            for (Association association : getAllAssociations(address)) {
+                if (association.isNotifyOnDeviceNearby()) {
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, "Device " + address
+                                + " managed by " + association.getPackageName()
+                                + " is nearby on " + timestamp);
+                    }
+                    getDeviceListenerServiceConnector(association).run(
+                            service -> service.onDeviceAppeared(association.getDeviceMacAddress()));
+                }
+            }
+        }
+    }
+
+    private void onDeviceDisappeared(String address) {
+        boolean hasDeviceListeners = false;
+        for (Association association : getAllAssociations(address)) {
+            if (association.isNotifyOnDeviceNearby()) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "Device " + address
+                            + " managed by " + association.getPackageName()
+                            + " disappeared; last seen on " + mDevicesLastNearby.get(address));
+                }
+
+                getDeviceListenerServiceConnector(association).run(
+                        service -> service.onDeviceDisappeared(address));
+                hasDeviceListeners = true;
+            }
+        }
+
+        cancelUnbindDeviceListener(address);
+        if (hasDeviceListeners) {
+            mMainHandler.postDelayed(
+                    mUnbindDeviceListenersRunnable,
+                    mUnbindDeviceListenersRunnable.getJobId(address),
+                    DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS);
+        }
+    }
+
+    private void cancelUnbindDeviceListener(String address) {
+        mMainHandler.removeCallbacks(
+                mUnbindDeviceListenersRunnable, mUnbindDeviceListenersRunnable.getJobId(address));
+    }
+
+    private void initBleScanning() {
+        Log.i(LOG_TAG, "initBleScanning()");
+
+        boolean bluetoothReady = mBluetoothAdapter.registerServiceLifecycleCallback(
+                new BluetoothAdapter.ServiceLifecycleCallback() {
+                    @Override
+                    public void onBluetoothServiceUp() {
+                        Log.i(LOG_TAG, "Bluetooth stack is up");
+                        startBleScan();
+                    }
+
+                    @Override
+                    public void onBluetoothServiceDown() {
+                        Log.w(LOG_TAG, "Bluetooth stack is down");
+                    }
+                });
+        if (bluetoothReady) {
+            startBleScan();
+        }
+    }
+
+    void startBleScan() {
+        Log.i(LOG_TAG, "startBleScan()");
+
+        List<ScanFilter> filters = getBleScanFilters();
+        if (filters.isEmpty()) {
+            return;
+        }
+        BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
+        if (scanner == null) {
+            Log.w(LOG_TAG, "scanner == null (likely BLE isn't ON yet)");
+        } else {
+            scanner.startScan(
+                    filters,
+                    new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(),
+                    mBleScanCallback);
+        }
+    }
+
+    void restartBleScan() {
+        mBluetoothAdapter.getBluetoothLeScanner().stopScan(mBleScanCallback);
+        startBleScan();
+    }
+
+    private List<ScanFilter> getBleScanFilters() {
+        ArrayList<ScanFilter> result = new ArrayList<>();
+        ArraySet<String> addressesSeen = new ArraySet<>();
+        for (Association association : getAllAssociations()) {
+            String address = association.getDeviceMacAddress();
+            if (addressesSeen.contains(address)) {
+                continue;
+            }
+            if (association.isNotifyOnDeviceNearby()) {
+                result.add(new ScanFilter.Builder().setDeviceAddress(address).build());
+                addressesSeen.add(address);
+            }
+        }
+        return result;
     }
 
     private AndroidFuture<String> getDeviceProfilePermissionDescription(String deviceProfile) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 3933e37..2f88351 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -45,6 +45,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
@@ -1038,7 +1039,8 @@
         mNetworkRanker = new NetworkRanker();
         final NetworkRequest defaultInternetRequest = createDefaultInternetRequestForTransport(
                 -1, NetworkRequest.Type.REQUEST);
-        mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder());
+        mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder(),
+                null /* attributionTag */);
         mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
         mDefaultNetworkRequests.add(mDefaultRequest);
         mNetworkRequestInfoLogs.log("REGISTER " + mDefaultRequest);
@@ -1246,6 +1248,7 @@
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
         netCap.setSingleUid(uid);
         return netCap;
@@ -1255,6 +1258,7 @@
             int transportType, NetworkRequest.Type type) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
         if (transportType > -1) {
             netCap.addTransportType(transportType);
@@ -1308,7 +1312,7 @@
 
         if (enable) {
             handleRegisterNetworkRequest(new NetworkRequestInfo(
-                    null, networkRequest, new Binder()));
+                    null, networkRequest, new Binder(), null /* attributionTag */));
         } else {
             handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID,
                     /* callOnUnavailable */ false);
@@ -1643,7 +1647,7 @@
 
     @Override
     public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
-                int userId, String callingPackageName) {
+                int userId, String callingPackageName, @Nullable String callingAttributionTag) {
         // The basic principle is: if an app's traffic could possibly go over a
         // network, without the app doing anything multinetwork-specific,
         // (hence, by "default"), then include that network's capabilities in
@@ -1674,7 +1678,8 @@
                 result.put(
                         nai.network,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, mDeps.getCallingUid(), callingPackageName));
+                                nc, mDeps.getCallingUid(), callingPackageName,
+                                callingAttributionTag));
             }
         }
 
@@ -1687,7 +1692,8 @@
                     result.put(
                             network,
                             createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                    nc, mDeps.getCallingUid(), callingPackageName));
+                                    nc, mDeps.getCallingUid(), callingPackageName,
+                                    callingAttributionTag));
                 }
             }
         }
@@ -1762,12 +1768,13 @@
     }
 
     @Override
-    public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
+    public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName,
+            @Nullable String callingAttributionTag) {
         mAppOpsManager.checkPackage(mDeps.getCallingUid(), callingPackageName);
         enforceAccessPermission();
         return createWithLocationInfoSanitizedIfNecessaryWhenParceled(
                 getNetworkCapabilitiesInternal(network),
-                mDeps.getCallingUid(), callingPackageName);
+                mDeps.getCallingUid(), callingPackageName, callingAttributionTag);
     }
 
     @VisibleForTesting
@@ -1786,11 +1793,12 @@
         return newNc;
     }
 
-    private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName) {
+    private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName,
+            @Nullable String callingAttributionTag) {
         final long token = Binder.clearCallingIdentity();
         try {
             return mLocationPermissionChecker.checkLocationPermission(
-                    callerPkgName, null /* featureId */, callerUid, null /* message */);
+                    callerPkgName, callingAttributionTag, callerUid, null /* message */);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -1799,7 +1807,8 @@
     @VisibleForTesting
     @Nullable
     NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-            @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) {
+            @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName,
+            @Nullable String callingAttributionTag) {
         if (nc == null) {
             return null;
         }
@@ -1808,7 +1817,8 @@
         // Avoid doing location permission check if the transport info has no location sensitive
         // data.
         if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) {
-            hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
+            hasLocationPermission =
+                    hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
             newNc = new NetworkCapabilities(nc, hasLocationPermission);
         } else {
             newNc = new NetworkCapabilities(nc, false /* parcelLocationSensitiveFields */);
@@ -1825,7 +1835,8 @@
         }
         if (hasLocationPermission == null) {
             // Location permission not checked yet, check now for masking owner UID.
-            hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
+            hasLocationPermission =
+                    hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
         }
         // Reset owner uid if the app has no location permission.
         if (!hasLocationPermission) {
@@ -1883,7 +1894,8 @@
         final ArrayList<NetworkState> result = new ArrayList<>();
         for (Network network : getAllNetworks()) {
             final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
-            if (nai != null) {
+            // TODO: Consider include SUSPENDED networks.
+            if (nai != null && nai.networkInfo.isConnected()) {
                 // TODO (b/73321673) : NetworkState contains a copy of the
                 // NetworkCapabilities, which may contain UIDs of apps to which the
                 // network applies. Should the UIDs be cleared so as not to leak or
@@ -5541,6 +5553,8 @@
         final int mPid;
         final int mUid;
         final Messenger messenger;
+        @Nullable
+        final String mCallingAttributionTag;
 
         /**
          * Get the list of UIDs this nri applies to.
@@ -5554,7 +5568,8 @@
             return uids;
         }
 
-        NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
+        NetworkRequestInfo(NetworkRequest r, PendingIntent pi,
+                @Nullable String callingAttributionTag) {
             mRequests = initializeRequests(r);
             ensureAllNetworkRequestsHaveType(mRequests);
             mPendingIntent = pi;
@@ -5563,9 +5578,11 @@
             mPid = getCallingPid();
             mUid = mDeps.getCallingUid();
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallingAttributionTag = callingAttributionTag;
         }
 
-        NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) {
+        NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder,
+                @Nullable String callingAttributionTag) {
             super();
             messenger = m;
             mRequests = initializeRequests(r);
@@ -5575,6 +5592,7 @@
             mUid = mDeps.getCallingUid();
             mPendingIntent = null;
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallingAttributionTag = callingAttributionTag;
 
             try {
                 mBinder.linkToDeath(this, 0);
@@ -5584,7 +5602,7 @@
         }
 
         NetworkRequestInfo(NetworkRequest r) {
-            this(r, null);
+            this(r, null /* pi */, null /* callingAttributionTag */);
         }
 
         // True if this NRI is being satisfied. It also accounts for if the nri has its satisifer
@@ -5777,7 +5795,8 @@
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
                 nextNetworkRequestId(), reqType);
-        NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
+        NetworkRequestInfo nri =
+                new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag);
         if (DBG) log("requestNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
@@ -5866,7 +5885,8 @@
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
                 nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
-        NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
+        NetworkRequestInfo nri =
+                new NetworkRequestInfo(networkRequest, operation, callingAttributionTag);
         if (DBG) log("pendingRequest for " + nri);
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT,
                 nri));
@@ -5910,7 +5930,8 @@
 
     @Override
     public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
-            Messenger messenger, IBinder binder, @NonNull String callingPackageName) {
+            Messenger messenger, IBinder binder, @NonNull String callingPackageName,
+            @Nullable String callingAttributionTag) {
         final int callingUid = mDeps.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
@@ -5930,7 +5951,8 @@
 
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
-        NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
+        NetworkRequestInfo nri =
+                new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag);
         if (VDBG) log("listenForNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -5939,7 +5961,8 @@
 
     @Override
     public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
-            PendingIntent operation, @NonNull String callingPackageName) {
+            PendingIntent operation, @NonNull String callingPackageName,
+            @Nullable String callingAttributionTag) {
         Objects.requireNonNull(operation, "PendingIntent cannot be null.");
         final int callingUid = mDeps.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
@@ -5953,7 +5976,8 @@
 
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
-        NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
+        NetworkRequestInfo nri =
+                new NetworkRequestInfo(networkRequest, operation, callingAttributionTag);
         if (VDBG) log("pendingListenForNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -7168,7 +7192,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, nri.mUid, nrForCallback.getRequestorPackageName()));
+                                nc, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                nri.mCallingAttributionTag));
                 putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
                         networkAgent.linkProperties, nri.mPid, nri.mUid));
                 // For this notification, arg1 contains the blocked status.
@@ -7187,7 +7212,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                netCap, nri.mUid, nrForCallback.getRequestorPackageName()));
+                                netCap, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                nri.mCallingAttributionTag));
                 break;
             }
             case ConnectivityManager.CALLBACK_IP_CHANGED: {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7d65156..1ad0176 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -890,7 +890,7 @@
             ZramWriteback.scheduleZramWriteback(mContext);
         }
 
-        updateTranscodeEnabled();
+        configureTranscoding();
     }
 
     /**
@@ -922,7 +922,7 @@
         }
     }
 
-    private void updateTranscodeEnabled() {
+    private void configureTranscoding() {
         // See MediaProvider TranscodeHelper#getBooleanProperty for more information
         boolean transcodeEnabled = false;
         boolean defaultValue = true;
@@ -935,6 +935,15 @@
                     "transcode_enabled", defaultValue);
         }
         SystemProperties.set("sys.fuse.transcode_enabled", String.valueOf(transcodeEnabled));
+
+        if (transcodeEnabled) {
+            LocalServices.getService(ActivityManagerInternal.class)
+                    .registerAnrController((packageName, uid) -> {
+                        // TODO: Retrieve delay from ExternalStorageService that can check
+                        // transcoding status
+                        return SystemProperties.getInt("sys.fuse.transcode_anr_delay_ms", 0);
+                    });
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index fe71fbf..c971bd2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2984,7 +2984,13 @@
                         pw.println("Reset all changes for " + packageName + " to default value.");
                         return 0;
                     }
-                    if (platformCompat.clearOverride(changeId, packageName)) {
+                    boolean existed;
+                    if (killPackage) {
+                        existed = platformCompat.clearOverride(changeId, packageName);
+                    } else {
+                        existed = platformCompat.clearOverrideForTest(changeId, packageName);
+                    }
+                    if (existed) {
                         pw.println("Reset change " + changeId + " for " + packageName
                                 + " to default value.");
                     } else {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c1c0c99..6f625a7 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -9266,6 +9266,18 @@
     }
 
     /**
+     * Update player session ID
+     * @param piid Player id to update
+     * @param sessionId The new audio session ID
+     */
+    public void playerSessionId(int piid, int sessionId) {
+        if (sessionId <= AudioSystem.AUDIO_SESSION_ALLOCATE) {
+            throw new IllegalArgumentException("invalid session Id " + sessionId);
+        }
+        mPlaybackMonitor.playerSessionId(piid, sessionId, Binder.getCallingUid());
+    }
+
+    /**
      * Update player event
      * @param piid Player id to update
      * @param event The new player event
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 36c67cd..68a084e 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -197,6 +197,28 @@
         }
     }
 
+    /**
+     * Update player session ID
+     * @param piid Player id to update
+     * @param sessionId The new audio session ID
+     * @param binderUid Calling binder uid
+     */
+    public void playerSessionId(int piid, int sessionId, int binderUid) {
+        final boolean change;
+        synchronized (mPlayerLock) {
+            final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
+            if (checkConfigurationCaller(piid, apc, binderUid)) {
+                change = apc.handleSessionIdEvent(sessionId);
+            } else {
+                Log.e(TAG, "Error updating audio session");
+                change = false;
+            }
+        }
+        if (change) {
+            dispatchPlaybackChange(false);
+        }
+    }
+
     private static final int FLAGS_FOR_SILENCE_OVERRIDE =
             AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
             AudioAttributes.FLAG_BYPASS_MUTE;
@@ -921,6 +943,7 @@
         private final int mClientUid;
         private final int mClientPid;
         private final AudioAttributes mPlayerAttr;
+        private final int mSessionId;
 
         NewPlayerEvent(AudioPlaybackConfiguration apc) {
             mPlayerIId = apc.getPlayerInterfaceId();
@@ -928,6 +951,7 @@
             mClientUid = apc.getClientUid();
             mClientPid = apc.getClientPid();
             mPlayerAttr = apc.getAudioAttributes();
+            mSessionId = apc.getSessionId();
         }
 
         @Override
@@ -935,7 +959,8 @@
             return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/"
                     + mClientPid + " type:"
                     + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
-                    + " attr:" + mPlayerAttr);
+                    + " attr:" + mPlayerAttr
+                    + " session:" + mSessionId);
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index e685ee2..1b6b9d7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -643,8 +643,7 @@
                 final Sensor sensor = mSensors.valueAt(i);
                 final int sensorId = mSensors.keyAt(i);
                 PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
-                sensor.getScheduler().recordCrashState();
-                sensor.getScheduler().reset();
+                sensor.onBinderDied();
             }
         });
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 640838c..baeb3fd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -33,7 +33,6 @@
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.keymaster.HardwareAuthToken;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.util.Slog;
@@ -63,7 +62,7 @@
 /**
  * Maintains the state of a single sensor within an instance of the {@link IFace} HAL.
  */
-public class Sensor implements IBinder.DeathRecipient {
+public class Sensor {
 
     private boolean mTestHalEnabled;
 
@@ -481,7 +480,6 @@
                 mTag, mScheduler, sensorId, userId, callback);
 
         final ISession newSession = daemon.createSession(sensorId, userId, resultController);
-        newSession.asBinder().linkToDeath(this, 0 /* flags */);
         mCurrentSession = new Session(mTag, newSession, userId, resultController);
     }
 
@@ -523,24 +521,21 @@
         proto.end(sensorToken);
     }
 
-    @Override
-    public void binderDied() {
-        Slog.e(mTag, "Binder died");
-        mHandler.post(() -> {
-            final BaseClientMonitor client = mScheduler.getCurrentClient();
-            if (client instanceof Interruptable) {
-                Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
-                final Interruptable interruptable = (Interruptable) client;
-                interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
-                        0 /* vendorCode */);
+    public void onBinderDied() {
+        final BaseClientMonitor client = mScheduler.getCurrentClient();
+        if (client instanceof Interruptable) {
+            Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+            final Interruptable interruptable = (Interruptable) client;
+            interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
+                    0 /* vendorCode */);
 
-                mScheduler.recordCrashState();
+            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                    BiometricsProtoEnums.MODALITY_FACE,
+                    BiometricsProtoEnums.ISSUE_HAL_DEATH);
+        }
 
-                FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
-                        BiometricsProtoEnums.MODALITY_FACE,
-                        BiometricsProtoEnums.ISSUE_HAL_DEATH);
-                mCurrentSession = null;
-            }
-        });
+        mScheduler.recordCrashState();
+        mScheduler.reset();
+        mCurrentSession = null;
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index ced46e1..0bd2f24 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -697,8 +697,7 @@
                 final Sensor sensor = mSensors.valueAt(i);
                 final int sensorId = mSensors.keyAt(i);
                 PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
-                sensor.getScheduler().recordCrashState();
-                sensor.getScheduler().reset();
+                sensor.onBinderDied();
             }
         });
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index f0e7e1c..7e4ee9e7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -31,7 +31,6 @@
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.keymaster.HardwareAuthToken;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.util.Slog;
@@ -65,7 +64,7 @@
  * {@link android.hardware.biometrics.fingerprint.IFingerprint} HAL.
  */
 @SuppressWarnings("deprecation")
-class Sensor implements IBinder.DeathRecipient {
+class Sensor {
 
     private boolean mTestHalEnabled;
 
@@ -461,7 +460,6 @@
                 mTag, mScheduler, sensorId, userId, callback);
 
         final ISession newSession = daemon.createSession(sensorId, userId, resultController);
-        newSession.asBinder().linkToDeath(this, 0 /* flags */);
         mCurrentSession = new Session(mTag, newSession, userId, resultController);
     }
 
@@ -503,24 +501,21 @@
         proto.end(sensorToken);
     }
 
-    @Override
-    public void binderDied() {
-        Slog.e(mTag, "Binder died");
-        mHandler.post(() -> {
-            final BaseClientMonitor client = mScheduler.getCurrentClient();
-            if (client instanceof Interruptable) {
-                Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
-                final Interruptable interruptable = (Interruptable) client;
-                interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
-                        0 /* vendorCode */);
+    public void onBinderDied() {
+        final BaseClientMonitor client = mScheduler.getCurrentClient();
+        if (client instanceof Interruptable) {
+            Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+            final Interruptable interruptable = (Interruptable) client;
+            interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                    0 /* vendorCode */);
 
-                mScheduler.recordCrashState();
+            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                    BiometricsProtoEnums.MODALITY_FINGERPRINT,
+                    BiometricsProtoEnums.ISSUE_HAL_DEATH);
+        }
 
-                FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
-                        BiometricsProtoEnums.MODALITY_FINGERPRINT,
-                        BiometricsProtoEnums.ISSUE_HAL_DEATH);
-                mCurrentSession = null;
-            }
-        });
+        mScheduler.recordCrashState();
+        mScheduler.reset();
+        mCurrentSession = null;
     }
 }
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 6b2a1c9..51ba5f7 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -211,9 +211,9 @@
     }
 
     @Override
-    public void clearOverrideForTest(long changeId, String packageName) {
+    public boolean clearOverrideForTest(long changeId, String packageName) {
         checkCompatChangeOverridePermission();
-        mCompatConfig.removeOverride(changeId, packageName);
+        return mCompatConfig.removeOverride(changeId, packageName);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0950d5d..01fee56 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2579,14 +2579,14 @@
         }
 
         @Override // Binder call
-        public void setTemporaryBrightness(float brightness) {
+        public void setTemporaryBrightness(int displayId, float brightness) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
                     "Permission required to set the display's brightness");
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mSyncRoot) {
-                    mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
+                    mDisplayPowerControllers.get(displayId)
                             .setTemporaryBrightness(brightness);
                 }
             } finally {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 7e6a137..73ebb2e 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -105,17 +105,18 @@
                 Slog.w(TAG, "No valid info found for display device " + physicalDisplayId);
                 return;
             }
-            SurfaceControl.DisplayConfig[] configs = SurfaceControl.getDisplayConfigs(displayToken);
-            if (configs == null) {
-                // There are no valid configs for this device, so we can't use it
-                Slog.w(TAG, "No valid configs found for display device " + physicalDisplayId);
+            SurfaceControl.DisplayMode[] displayModes =
+                    SurfaceControl.getDisplayModes(displayToken);
+            if (displayModes == null) {
+                // There are no valid modes for this device, so we can't use it
+                Slog.w(TAG, "No valid modes found for display device " + physicalDisplayId);
                 return;
             }
-            int activeConfig = SurfaceControl.getActiveConfig(displayToken);
-            if (activeConfig < 0) {
-                // There is no active config, and for now we don't have the
+            int activeDisplayMode = SurfaceControl.getActiveDisplayMode(displayToken);
+            if (activeDisplayMode < 0) {
+                // There is no active mode, and for now we don't have the
                 // policy to set one.
-                Slog.w(TAG, "No active config found for display device " + physicalDisplayId);
+                Slog.w(TAG, "No active mode found for display device " + physicalDisplayId);
                 return;
             }
             int activeColorMode = SurfaceControl.getActiveColorMode(displayToken);
@@ -127,8 +128,8 @@
                         physicalDisplayId);
                 activeColorMode = Display.COLOR_MODE_INVALID;
             }
-            SurfaceControl.DesiredDisplayConfigSpecs configSpecs =
-                    SurfaceControl.getDesiredDisplayConfigSpecs(displayToken);
+            SurfaceControl.DesiredDisplayModeSpecs modeSpecsSpecs =
+                    SurfaceControl.getDesiredDisplayModeSpecs(displayToken);
             int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
             Display.HdrCapabilities hdrCapabilities =
                     SurfaceControl.getHdrCapabilities(displayToken);
@@ -136,13 +137,13 @@
             if (device == null) {
                 // Display was added.
                 final boolean isDefaultDisplay = mDevices.size() == 0;
-                device = new LocalDisplayDevice(displayToken, physicalDisplayId, info,
-                        configs, activeConfig, configSpecs, colorModes, activeColorMode,
+                device = new LocalDisplayDevice(displayToken, physicalDisplayId, info, displayModes,
+                        activeDisplayMode, modeSpecsSpecs, colorModes, activeColorMode,
                         hdrCapabilities, isDefaultDisplay);
                 mDevices.put(physicalDisplayId, device);
                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
-            } else if (device.updateDisplayPropertiesLocked(info, configs, activeConfig,
-                    configSpecs, colorModes, activeColorMode, hdrCapabilities)) {
+            } else if (device.updateDisplayPropertiesLocked(info, displayModes, activeDisplayMode,
+                    modeSpecsSpecs, colorModes, activeColorMode, hdrCapabilities)) {
                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
             }
         } else {
@@ -189,12 +190,12 @@
         // This is only set in the runnable returned from requestDisplayStateLocked.
         private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         private int mDefaultModeId;
-        private int mDefaultConfigGroup;
+        private int mDefaultModeGroup;
         private int mActiveModeId;
         private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
                 new DisplayModeDirector.DesiredDisplayModeSpecs();
         private boolean mDisplayModeSpecsInvalid;
-        private int mActiveConfigId;
+        private int mActiveDisplayModeId;
         private int mActiveColorMode;
         private Display.HdrCapabilities mHdrCapabilities;
         private boolean mAllmSupported;
@@ -204,7 +205,7 @@
         private boolean mSidekickActive;
         private SidekickInternal mSidekickInternal;
         private SurfaceControl.DisplayInfo mDisplayInfo;
-        private SurfaceControl.DisplayConfig[] mDisplayConfigs;
+        private SurfaceControl.DisplayMode[] mDisplayModes;
         private Spline mSystemBrightnessToNits;
         private Spline mNitsToHalBrightness;
         private DisplayDeviceConfig mDisplayDeviceConfig;
@@ -213,15 +214,15 @@
                 new DisplayEventReceiver.FrameRateOverride[0];
 
         LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
-                SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs,
-                int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
+                SurfaceControl.DisplayInfo info, SurfaceControl.DisplayMode[] displayModes,
+                int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs,
                 int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities,
                 boolean isDefaultDisplay) {
             super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
             mPhysicalDisplayId = physicalDisplayId;
             mIsDefaultDisplay = isDefaultDisplay;
-            updateDisplayPropertiesLocked(info, configs, activeConfigId, configSpecs, colorModes,
-                    activeColorMode, hdrCapabilities);
+            updateDisplayPropertiesLocked(info, displayModes, activeDisplayModeId, modeSpecs,
+                    colorModes, activeColorMode, hdrCapabilities);
             mSidekickInternal = LocalServices.getService(SidekickInternal.class);
             mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay);
             mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
@@ -241,10 +242,11 @@
          * Returns true if there is a change.
          **/
         public boolean updateDisplayPropertiesLocked(SurfaceControl.DisplayInfo info,
-                SurfaceControl.DisplayConfig[] configs,
-                int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
+                SurfaceControl.DisplayMode[] displayModes,
+                int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs,
                 int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities) {
-            boolean changed = updateDisplayConfigsLocked(configs, activeConfigId, configSpecs);
+            boolean changed = updateDisplayModesLocked(
+                    displayModes, activeDisplayModeId, modeSpecs);
             changed |= updateDisplayInfo(info);
             changed |= updateColorModesLocked(colorModes, activeColorMode);
             changed |= updateHdrCapabilitiesLocked(hdrCapabilities);
@@ -255,35 +257,35 @@
             return changed;
         }
 
-        public boolean updateDisplayConfigsLocked(
-                SurfaceControl.DisplayConfig[] configs, int activeConfigId,
-                SurfaceControl.DesiredDisplayConfigSpecs configSpecs) {
-            mDisplayConfigs = Arrays.copyOf(configs, configs.length);
-            mActiveConfigId = activeConfigId;
+        public boolean updateDisplayModesLocked(
+                SurfaceControl.DisplayMode[] displayModes, int activeDisplayModeId,
+                SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
+            mDisplayModes = Arrays.copyOf(displayModes, displayModes.length);
+            mActiveDisplayModeId = activeDisplayModeId;
             // Build an updated list of all existing modes.
             ArrayList<DisplayModeRecord> records = new ArrayList<>();
             boolean modesAdded = false;
-            for (int i = 0; i < configs.length; i++) {
-                SurfaceControl.DisplayConfig config = configs[i];
+            for (int i = 0; i < displayModes.length; i++) {
+                SurfaceControl.DisplayMode mode = displayModes[i];
                 List<Float> alternativeRefreshRates = new ArrayList<>();
-                for (int j = 0; j < configs.length; j++) {
-                    SurfaceControl.DisplayConfig other = configs[j];
-                    boolean isAlternative = j != i && other.width == config.width
-                            && other.height == config.height
-                            && other.refreshRate != config.refreshRate
-                            && other.configGroup == config.configGroup;
+                for (int j = 0; j < displayModes.length; j++) {
+                    SurfaceControl.DisplayMode other = displayModes[j];
+                    boolean isAlternative = j != i && other.width == mode.width
+                            && other.height == mode.height
+                            && other.refreshRate != mode.refreshRate
+                            && other.group == mode.group;
                     if (isAlternative) {
-                        alternativeRefreshRates.add(configs[j].refreshRate);
+                        alternativeRefreshRates.add(displayModes[j].refreshRate);
                     }
                 }
                 Collections.sort(alternativeRefreshRates);
 
                 // First, check to see if we've already added a matching mode. Since not all
                 // configuration options are exposed via Display.Mode, it's possible that we have
-                // multiple DisplayConfigs that would generate the same Display.Mode.
+                // multiple DisplayModess that would generate the same Display.Mode.
                 boolean existingMode = false;
                 for (DisplayModeRecord record : records) {
-                    if (record.hasMatchingMode(config)
+                    if (record.hasMatchingMode(mode)
                             && refreshRatesEquals(alternativeRefreshRates,
                                     record.mMode.getAlternativeRefreshRates())) {
                         existingMode = true;
@@ -296,13 +298,13 @@
                 // If we haven't already added a mode for this configuration to the new set of
                 // supported modes then check to see if we have one in the prior set of supported
                 // modes to reuse.
-                DisplayModeRecord record = findDisplayModeRecord(config, alternativeRefreshRates);
+                DisplayModeRecord record = findDisplayModeRecord(mode, alternativeRefreshRates);
                 if (record == null) {
                     float[] alternativeRates = new float[alternativeRefreshRates.size()];
                     for (int j = 0; j < alternativeRates.length; j++) {
                         alternativeRates[j] = alternativeRefreshRates.get(j);
                     }
-                    record = new DisplayModeRecord(config, alternativeRates);
+                    record = new DisplayModeRecord(mode, alternativeRates);
                     modesAdded = true;
                 }
                 records.add(record);
@@ -312,7 +314,7 @@
             DisplayModeRecord activeRecord = null;
             for (int i = 0; i < records.size(); i++) {
                 DisplayModeRecord record = records.get(i);
-                if (record.hasMatchingMode(configs[activeConfigId])) {
+                if (record.hasMatchingMode(displayModes[activeDisplayModeId])) {
                     activeRecord = record;
                     break;
                 }
@@ -334,20 +336,20 @@
             // Check whether surface flinger spontaneously changed display config specs out from
             // under us. If so, schedule a traversal to reapply our display config specs.
             if (mDisplayModeSpecs.baseModeId != NO_DISPLAY_MODE_ID) {
-                int activeBaseMode = findMatchingModeIdLocked(configSpecs.defaultConfig);
-                // If we can't map the defaultConfig index to a mode, then the physical display
-                // configs must have changed, and the code below for handling changes to the
-                // list of available modes will take care of updating display config specs.
+                int activeBaseMode = findMatchingModeIdLocked(modeSpecs.defaultMode);
+                // If we can't map the defaultMode index to a mode, then the physical display
+                // modes must have changed, and the code below for handling changes to the
+                // list of available modes will take care of updating display mode specs.
                 if (activeBaseMode != NO_DISPLAY_MODE_ID) {
                     if (mDisplayModeSpecs.baseModeId != activeBaseMode
                             || mDisplayModeSpecs.primaryRefreshRateRange.min
-                                    != configSpecs.primaryRefreshRateMin
+                                    != modeSpecs.primaryRefreshRateMin
                             || mDisplayModeSpecs.primaryRefreshRateRange.max
-                                    != configSpecs.primaryRefreshRateMax
+                                    != modeSpecs.primaryRefreshRateMax
                             || mDisplayModeSpecs.appRequestRefreshRateRange.min
-                                    != configSpecs.appRequestRefreshRateMin
+                                    != modeSpecs.appRequestRefreshRateMin
                             || mDisplayModeSpecs.appRequestRefreshRateRange.max
-                                    != configSpecs.appRequestRefreshRateMax) {
+                                    != modeSpecs.appRequestRefreshRateMax) {
                         mDisplayModeSpecsInvalid = true;
                         sendTraversalRequestLocked();
                     }
@@ -368,17 +370,17 @@
             // For a new display, we need to initialize the default mode ID.
             if (mDefaultModeId == NO_DISPLAY_MODE_ID) {
                 mDefaultModeId = activeRecord.mMode.getModeId();
-                mDefaultConfigGroup = configs[activeConfigId].configGroup;
+                mDefaultModeGroup = displayModes[activeDisplayModeId].group;
             } else if (modesAdded && activeModeChanged) {
                 Slog.d(TAG, "New display modes are added and the active mode has changed, "
                         + "use active mode as default mode.");
                 mDefaultModeId = activeRecord.mMode.getModeId();
-                mDefaultConfigGroup = configs[activeConfigId].configGroup;
-            } else if (findDisplayConfigIdLocked(mDefaultModeId, mDefaultConfigGroup) < 0) {
+                mDefaultModeGroup = displayModes[activeDisplayModeId].group;
+            } else if (findDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) {
                 Slog.w(TAG, "Default display mode no longer available, using currently"
                         + " active mode as default.");
                 mDefaultModeId = activeRecord.mMode.getModeId();
-                mDefaultConfigGroup = configs[activeConfigId].configGroup;
+                mDefaultModeGroup = displayModes[activeDisplayModeId].group;
             }
 
             // Determine whether the display mode specs' base mode is still there.
@@ -518,11 +520,11 @@
             return true;
         }
 
-        private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayConfig config,
+        private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayMode mode,
                 List<Float> alternativeRefreshRates) {
             for (int i = 0; i < mSupportedModes.size(); i++) {
                 DisplayModeRecord record = mSupportedModes.valueAt(i);
-                if (record.hasMatchingMode(config)
+                if (record.hasMatchingMode(mode)
                         && refreshRatesEquals(alternativeRefreshRates,
                                 record.mMode.getAlternativeRefreshRates())) {
                     return record;
@@ -554,10 +556,10 @@
         @Override
         public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
             if (mInfo == null) {
-                SurfaceControl.DisplayConfig config = mDisplayConfigs[mActiveConfigId];
+                SurfaceControl.DisplayMode mode = mDisplayModes[mActiveDisplayModeId];
                 mInfo = new DisplayDeviceInfo();
-                mInfo.width = config.width;
-                mInfo.height = config.height;
+                mInfo.width = mode.width;
+                mInfo.height = mode.height;
                 mInfo.modeId = mActiveModeId;
                 mInfo.defaultModeId = mDefaultModeId;
                 mInfo.supportedModes = getDisplayModes(mSupportedModes);
@@ -570,16 +572,16 @@
                     mInfo.supportedColorModes[i] = mSupportedColorModes.get(i);
                 }
                 mInfo.hdrCapabilities = mHdrCapabilities;
-                mInfo.appVsyncOffsetNanos = config.appVsyncOffsetNanos;
-                mInfo.presentationDeadlineNanos = config.presentationDeadlineNanos;
+                mInfo.appVsyncOffsetNanos = mode.appVsyncOffsetNanos;
+                mInfo.presentationDeadlineNanos = mode.presentationDeadlineNanos;
                 mInfo.state = mState;
                 mInfo.uniqueId = getUniqueId();
                 final DisplayAddress.Physical physicalAddress =
                         DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
                 mInfo.address = physicalAddress;
                 mInfo.densityDpi = (int) (mDisplayInfo.density * 160 + 0.5f);
-                mInfo.xDpi = config.xDpi;
-                mInfo.yDpi = config.yDpi;
+                mInfo.xDpi = mode.xDpi;
+                mInfo.yDpi = mode.yDpi;
                 mInfo.deviceProductInfo = mDisplayInfo.deviceProductInfo;
 
                 // Assume that all built-in displays that have secure output (eg. HDCP) also
@@ -835,16 +837,16 @@
                 return;
             }
 
-            // Find the config Id based on the desired mode specs. In case there is more than one
-            // config matching the mode spec, prefer the one that is in the default config group.
-            // For now the default config group is taken from the active config when we got the
+            // Find the mode Id based on the desired mode specs. In case there is more than one
+            // mode matching the mode spec, prefer the one that is in the default mode group.
+            // For now the default config mode is taken from the active mode when we got the
             // hotplug event for the display. In the future we might want to change the default
-            // config based on vendor requirements.
-            // Note: We prefer the default config group over the current one as this is the config
+            // mode based on vendor requirements.
+            // Note: We prefer the default mode group over the current one as this is the mode
             // group the vendor prefers.
-            int baseConfigId = findDisplayConfigIdLocked(displayModeSpecs.baseModeId,
-                    mDefaultConfigGroup);
-            if (baseConfigId < 0) {
+            int baseModeId = findDisplayModeIdLocked(displayModeSpecs.baseModeId,
+                    mDefaultModeGroup);
+            if (baseModeId < 0) {
                 // When a display is hotplugged, it's possible for a mode to be removed that was
                 // previously valid. Because of the way display changes are propagated through the
                 // framework, and the caching of the display mode specs in LogicalDisplay, it's
@@ -862,7 +864,7 @@
                 getHandler().sendMessage(PooledLambda.obtainMessage(
                         LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
                         getDisplayTokenLocked(),
-                        new SurfaceControl.DesiredDisplayConfigSpecs(baseConfigId,
+                        new SurfaceControl.DesiredDisplayModeSpecs(baseModeId,
                                 mDisplayModeSpecs.allowGroupSwitching,
                                 mDisplayModeSpecs.primaryRefreshRateRange.min,
                                 mDisplayModeSpecs.primaryRefreshRateRange.max,
@@ -872,13 +874,13 @@
         }
 
         private void setDesiredDisplayModeSpecsAsync(IBinder displayToken,
-                SurfaceControl.DesiredDisplayConfigSpecs configSpecs) {
+                SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
             // Do not lock when calling these SurfaceControl methods because they are sync
             // operations that may block for a while when setting display power mode.
-            SurfaceControl.setDesiredDisplayConfigSpecs(displayToken, configSpecs);
-            final int activePhysIndex = SurfaceControl.getActiveConfig(displayToken);
+            SurfaceControl.setDesiredDisplayModeSpecs(displayToken, modeSpecs);
+            final int activeMode = SurfaceControl.getActiveDisplayMode(displayToken);
             synchronized (getSyncRoot()) {
-                if (updateActiveModeLocked(activePhysIndex)) {
+                if (updateActiveModeLocked(activeMode)) {
                     updateDeviceInfoLocked();
                 }
             }
@@ -889,8 +891,8 @@
             updateDeviceInfoLocked();
         }
 
-        public void onActiveDisplayConfigChangedLocked(int configId) {
-            if (updateActiveModeLocked(configId)) {
+        public void onActiveDisplayModeChangedLocked(int modeId) {
+            if (updateActiveModeLocked(modeId)) {
                 updateDeviceInfoLocked();
             }
         }
@@ -902,15 +904,15 @@
             }
         }
 
-        public boolean updateActiveModeLocked(int activeConfigId) {
-            if (mActiveConfigId == activeConfigId) {
+        public boolean updateActiveModeLocked(int activeModeId) {
+            if (mActiveDisplayModeId == activeModeId) {
                 return false;
             }
-            mActiveConfigId = activeConfigId;
-            mActiveModeId = findMatchingModeIdLocked(activeConfigId);
+            mActiveDisplayModeId = activeModeId;
+            mActiveModeId = findMatchingModeIdLocked(activeModeId);
             if (mActiveModeId == NO_DISPLAY_MODE_ID) {
-                Slog.w(TAG, "In unknown mode after setting allowed configs"
-                        + ", activeConfigId=" + mActiveConfigId);
+                Slog.w(TAG, "In unknown mode after setting allowed modes"
+                        + ", activeModeId=" + mActiveDisplayModeId);
             }
             return true;
         }
@@ -990,7 +992,7 @@
             pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId);
             pw.println("mDisplayModeSpecs={" + mDisplayModeSpecs + "}");
             pw.println("mDisplayModeSpecsInvalid=" + mDisplayModeSpecsInvalid);
-            pw.println("mActiveConfigId=" + mActiveConfigId);
+            pw.println("mActiveDisplayModeId=" + mActiveDisplayModeId);
             pw.println("mActiveModeId=" + mActiveModeId);
             pw.println("mActiveColorMode=" + mActiveColorMode);
             pw.println("mDefaultModeId=" + mDefaultModeId);
@@ -1002,9 +1004,9 @@
             pw.println("mGameContentTypeSupported=" + mGameContentTypeSupported);
             pw.println("mGameContentTypeRequested=" + mGameContentTypeRequested);
             pw.println("mDisplayInfo=" + mDisplayInfo);
-            pw.println("mDisplayConfigs=");
-            for (int i = 0; i < mDisplayConfigs.length; i++) {
-                pw.println("  " + mDisplayConfigs[i]);
+            pw.println("mDisplayModes=");
+            for (int i = 0; i < mDisplayModes.length; i++) {
+                pw.println("  " + mDisplayModes[i]);
             }
             pw.println("mSupportedModes=");
             for (int i = 0; i < mSupportedModes.size(); i++) {
@@ -1014,37 +1016,37 @@
             pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
         }
 
-        private int findDisplayConfigIdLocked(int modeId, int configGroup) {
-            int matchingConfigId = SurfaceControl.DisplayConfig.INVALID_DISPLAY_CONFIG_ID;
+        private int findDisplayModeIdLocked(int modeId, int modeGroup) {
+            int matchingModeId = SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID;
             DisplayModeRecord record = mSupportedModes.get(modeId);
             if (record != null) {
-                for (int i = 0; i < mDisplayConfigs.length; i++) {
-                    SurfaceControl.DisplayConfig config = mDisplayConfigs[i];
-                    if (record.hasMatchingMode(config)) {
-                        if (matchingConfigId
-                                == SurfaceControl.DisplayConfig.INVALID_DISPLAY_CONFIG_ID) {
-                            matchingConfigId = i;
+                for (int i = 0; i < mDisplayModes.length; i++) {
+                    SurfaceControl.DisplayMode mode = mDisplayModes[i];
+                    if (record.hasMatchingMode(mode)) {
+                        if (matchingModeId
+                                == SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID) {
+                            matchingModeId = i;
                         }
 
-                        // Prefer to return a config that matches the configGroup
-                        if (config.configGroup == configGroup) {
+                        // Prefer to return a mode that matches the modeGroup
+                        if (mode.group == modeGroup) {
                             return i;
                         }
                     }
                 }
             }
-            return matchingConfigId;
+            return matchingModeId;
         }
 
-        private int findMatchingModeIdLocked(int configId) {
-            if (configId < 0 || configId >= mDisplayConfigs.length) {
-                Slog.e(TAG, "Invalid display config index " + configId);
+        private int findMatchingModeIdLocked(int modeId) {
+            if (modeId < 0 || modeId >= mDisplayModes.length) {
+                Slog.e(TAG, "Invalid display config index " + modeId);
                 return NO_DISPLAY_MODE_ID;
             }
-            SurfaceControl.DisplayConfig config = mDisplayConfigs[configId];
+            SurfaceControl.DisplayMode mode = mDisplayModes[modeId];
             for (int i = 0; i < mSupportedModes.size(); i++) {
                 DisplayModeRecord record = mSupportedModes.valueAt(i);
-                if (record.hasMatchingMode(config)) {
+                if (record.hasMatchingMode(mode)) {
                     return record.mMode.getModeId();
                 }
             }
@@ -1091,30 +1093,29 @@
     }
 
     /**
-     * Keeps track of a display configuration.
+     * Keeps track of a display mode.
      */
     private static final class DisplayModeRecord {
         public final Display.Mode mMode;
 
-        DisplayModeRecord(SurfaceControl.DisplayConfig config,
+        DisplayModeRecord(SurfaceControl.DisplayMode mode,
                 float[] alternativeRefreshRates) {
-            mMode = createMode(config.width, config.height, config.refreshRate,
+            mMode = createMode(mode.width, mode.height, mode.refreshRate,
                     alternativeRefreshRates);
         }
 
         /**
-         * Returns whether the mode generated by the given DisplayConfig matches the mode
+         * Returns whether the mode generated by the given DisplayModes matches the mode
          * contained by the record modulo mode ID.
          *
-         * Note that this doesn't necessarily mean that the DisplayConfigs are identical, just
+         * Note that this doesn't necessarily mean that the DisplayModes are identical, just
          * that they generate identical modes.
          */
-        public boolean hasMatchingMode(SurfaceControl.DisplayConfig config) {
-            int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate());
-            int configRefreshRate = Float.floatToIntBits(config.refreshRate);
-            return mMode.getPhysicalWidth() == config.width
-                    && mMode.getPhysicalHeight() == config.height
-                    && modeRefreshRate == configRefreshRate;
+        public boolean hasMatchingMode(SurfaceControl.DisplayMode mode) {
+            return mMode.getPhysicalWidth() == mode.width
+                    && mMode.getPhysicalHeight() == mode.height
+                    && Float.floatToIntBits(mMode.getRefreshRate())
+                        == Float.floatToIntBits(mode.refreshRate);
         }
 
         public String toString() {
@@ -1131,7 +1132,7 @@
 
     public interface DisplayEventListener {
         void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected);
-        void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId);
+        void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId);
         void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
                 DisplayEventReceiver.FrameRateOverride[] overrides);
 
@@ -1141,7 +1142,7 @@
         private final DisplayEventListener mListener;
         ProxyDisplayEventReceiver(Looper looper, DisplayEventListener listener) {
             super(looper, VSYNC_SOURCE_APP,
-                    EVENT_REGISTRATION_CONFIG_CHANGED_FLAG
+                    EVENT_REGISTRATION_MODE_CHANGED_FLAG
                             | EVENT_REGISTRATION_FRAME_RATE_OVERRIDE_FLAG);
             mListener = listener;
         }
@@ -1152,8 +1153,8 @@
         }
 
         @Override
-        public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
-            mListener.onConfigChanged(timestampNanos, physicalDisplayId, configId);
+        public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
+            mListener.onModeChanged(timestampNanos, physicalDisplayId, modeId);
         }
 
         @Override
@@ -1176,23 +1177,23 @@
         }
 
         @Override
-        public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+        public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
             if (DEBUG) {
-                Slog.d(TAG, "onConfigChanged("
+                Slog.d(TAG, "onModeChanged("
                         + "timestampNanos=" + timestampNanos
                         + ", physicalDisplayId=" + physicalDisplayId
-                        + ", configId=" + configId + ")");
+                        + ", modeId=" + modeId + ")");
             }
             synchronized (getSyncRoot()) {
                 LocalDisplayDevice device = mDevices.get(physicalDisplayId);
                 if (device == null) {
                     if (DEBUG) {
-                        Slog.d(TAG, "Received config change for unhandled physical display: "
+                        Slog.d(TAG, "Received mode change for unhandled physical display: "
                                 + "physicalDisplayId=" + physicalDisplayId);
                     }
                     return;
                 }
-                device.onActiveDisplayConfigChangedLocked(configId);
+                device.onActiveDisplayModeChangedLocked(modeId);
             }
         }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 0ae1994..bdf92ca 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -541,7 +541,7 @@
     private void bootCompleted() {
         // on boot, if device is interactive, set HDMI CEC state as powered on as well
         if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
-            onWakeUp(WAKE_UP_BOOT_UP);
+            mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
         }
     }
 
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 2e4200c..4e97411 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -57,6 +57,7 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IVibratorStateListener;
 import android.os.InputEventInjectionResult;
 import android.os.InputEventInjectionSync;
 import android.os.LocaleList;
@@ -64,6 +65,7 @@
 import android.os.Message;
 import android.os.MessageQueue;
 import android.os.Process;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
@@ -77,6 +79,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.view.Display;
 import android.view.IInputFilter;
 import android.view.IInputFilterHost;
@@ -100,6 +103,7 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
@@ -221,13 +225,16 @@
     private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>();
     private int mNextVibratorTokenValue;
 
+    // List of currently registered vibrator state changed listeners by device id.
+    @GuardedBy("mVibratorLock")
+    private final SparseArray<RemoteCallbackList<IVibratorStateListener>> mVibratorStateListeners =
+            new SparseArray<RemoteCallbackList<IVibratorStateListener>>();
+    // List of vibrator states by device id.
+    @GuardedBy("mVibratorLock")
+    private final SparseBooleanArray mIsVibrating = new SparseBooleanArray();
+
     // State for lid switch
-    // Lock for the lid switch state. Held when triggering callbacks to guarantee lid switch events
-    // are delivered in order. For ex, when a new lid switch callback is registered the lock is held
-    // while the callback is processing the initial lid switch event which guarantees that any
-    // events that occur at the same time are delivered after the callback has returned.
     private final Object mLidSwitchLock = new Object();
-    @GuardedBy("mLidSwitchLock")
     private List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>();
 
     // State for the currently installed input filter.
@@ -375,6 +382,9 @@
     public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
     public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
 
+    /** Indicates an open state for the lid switch. */
+    public static final int SW_STATE_LID_OPEN = 0;
+
     /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
     final boolean mUseDevInputEventForAudioJack;
 
@@ -410,18 +420,13 @@
     }
 
     void registerLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
+        boolean lidOpen;
         synchronized (mLidSwitchLock) {
             mLidSwitchCallbacks.add(callback);
-
-            // Skip triggering the initial callback if the system is not yet ready as the switch
-            // state will be reported as KEY_STATE_UNKNOWN. The callback will be triggered in
-            // systemRunning().
-            if (mSystemReady) {
-                boolean lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)
-                        == KEY_STATE_UP;
-                callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);
-            }
+            lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)
+                    == SW_STATE_LID_OPEN;
         }
+        callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);
     }
 
     void unregisterLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
@@ -469,18 +474,7 @@
         }
         mNotificationManager = (NotificationManager)mContext.getSystemService(
                 Context.NOTIFICATION_SERVICE);
-
-        synchronized (mLidSwitchLock) {
-            mSystemReady = true;
-
-            // Send the initial lid switch state to any callback registered before the system was
-            // ready.
-            int switchState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID);
-            for (int i = 0; i < mLidSwitchCallbacks.size(); i++) {
-                LidSwitchCallback callback = mLidSwitchCallbacks.get(i);
-                callback.notifyLidSwitchChanged(0 /* whenNanos */, switchState == KEY_STATE_UP);
-            }
-        }
+        mSystemReady = true;
 
         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -2008,6 +2002,92 @@
         }
     }
 
+    // Native callback.
+    private void notifyVibratorState(int deviceId, boolean isOn) {
+        if (DEBUG) {
+            Slog.d(TAG, "notifyVibratorState: deviceId=" + deviceId + " isOn=" + isOn);
+        }
+        synchronized (mVibratorLock) {
+            mIsVibrating.put(deviceId, isOn);
+            notifyVibratorStateListenersLocked(deviceId);
+        }
+    }
+
+    @GuardedBy("mVibratorLock")
+    private void notifyVibratorStateListenersLocked(int deviceId) {
+        if (!mVibratorStateListeners.contains(deviceId)) {
+            if (DEBUG) {
+                Slog.v(TAG, "Device " + deviceId + " doesn't have vibrator state listener.");
+            }
+            return;
+        }
+        RemoteCallbackList<IVibratorStateListener> listeners =
+                mVibratorStateListeners.get(deviceId);
+        final int length = listeners.beginBroadcast();
+        try {
+            for (int i = 0; i < length; i++) {
+                notifyVibratorStateListenerLocked(deviceId, listeners.getBroadcastItem(i));
+            }
+        } finally {
+            listeners.finishBroadcast();
+        }
+    }
+
+    @GuardedBy("mVibratorLock")
+    private void notifyVibratorStateListenerLocked(int deviceId, IVibratorStateListener listener) {
+        try {
+            listener.onVibrating(mIsVibrating.get(deviceId));
+        } catch (RemoteException | RuntimeException e) {
+            Slog.e(TAG, "Vibrator state listener failed to call", e);
+        }
+    }
+
+    @Override // Binder call
+    public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+        Preconditions.checkNotNull(listener, "listener must not be null");
+
+        RemoteCallbackList<IVibratorStateListener> listeners;
+        synchronized (mVibratorLock) {
+            if (!mVibratorStateListeners.contains(deviceId)) {
+                listeners = new RemoteCallbackList<>();
+                mVibratorStateListeners.put(deviceId, listeners);
+            } else {
+                listeners = mVibratorStateListeners.get(deviceId);
+            }
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (!listeners.register(listener)) {
+                    Slog.e(TAG, "Could not register vibrator state listener " + listener);
+                    return false;
+                }
+                // Notify its callback after new client registered.
+                notifyVibratorStateListenerLocked(deviceId, listener);
+                return true;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
+
+    @Override // Binder call
+    public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+        synchronized (mVibratorLock) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (!mVibratorStateListeners.contains(deviceId)) {
+                    Slog.w(TAG, "Vibrator state listener " + deviceId + " doesn't exist");
+                    return false;
+                }
+                RemoteCallbackList<IVibratorStateListener> listeners =
+                        mVibratorStateListeners.get(deviceId);
+                return listeners.unregister(listener);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
+
     // Binder call
     @Override
     public int getBatteryStatus(int deviceId) {
@@ -2251,13 +2331,14 @@
 
         if ((switchMask & SW_LID_BIT) != 0) {
             final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
+
+            ArrayList<LidSwitchCallback> callbacksCopy;
             synchronized (mLidSwitchLock) {
-                if (mSystemReady) {
-                    for (int i = 0; i < mLidSwitchCallbacks.size(); i++) {
-                        LidSwitchCallback callbacks = mLidSwitchCallbacks.get(i);
-                        callbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
-                    }
-                }
+                callbacksCopy = new ArrayList<>(mLidSwitchCallbacks);
+            }
+            for (int i = 0; i < callbacksCopy.size(); i++) {
+                LidSwitchCallback callbacks = callbacksCopy.get(i);
+                callbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
             }
         }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6f7f69e..1a4c8b7 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5227,6 +5227,8 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
         boolean asProto = false;
         for (int argIndex = 0; argIndex < args.length; argIndex++) {
             if (args[argIndex].equals(PROTO_ARG)) {
@@ -5249,8 +5251,6 @@
     }
 
     private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-
         if (useProto) {
             final ProtoOutputStream proto = new ProtoOutputStream(fd);
             dumpDebug(proto, InputMethodManagerServiceTraceProto.INPUT_METHOD_MANAGER_SERVICE);
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 142f64f..ea759bf 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -66,6 +66,7 @@
 import android.location.LocationProvider;
 import android.location.LocationRequest;
 import android.location.LocationTime;
+import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
 import android.location.util.identity.CallerIdentity;
 import android.os.Binder;
@@ -882,6 +883,20 @@
     }
 
     @Override
+    public void addProviderRequestListener(IProviderRequestListener listener) {
+        for (LocationProviderManager manager : mProviderManagers) {
+            manager.addProviderRequestListener(listener);
+        }
+    }
+
+    @Override
+    public void removeProviderRequestListener(IProviderRequestListener listener) {
+        for (LocationProviderManager manager : mProviderManagers) {
+            manager.removeProviderRequestListener(listener);
+        }
+    }
+
+    @Override
     public void injectGnssMeasurementCorrections(GnssMeasurementCorrections corrections) {
         if (mGnssManagerService != null) {
             mGnssManagerService.injectGnssMeasurementCorrections(corrections);
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 221d4fb..48a012e 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -55,6 +55,7 @@
 import android.location.LocationManagerInternal.ProviderEnabledListener;
 import android.location.LocationRequest;
 import android.location.LocationResult;
+import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
 import android.location.provider.ProviderRequest;
 import android.location.util.identity.CallerIdentity;
@@ -117,6 +118,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Predicate;
 
 /**
@@ -1219,6 +1221,8 @@
     @GuardedBy("mLock")
     private final ArrayList<ProviderEnabledListener> mEnabledListeners;
 
+    private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners;
+
     protected final LocationManagerInternal mLocationManagerInternal;
     protected final SettingsHelper mSettingsHelper;
     protected final UserInfoHelper mUserHelper;
@@ -1279,6 +1283,7 @@
         mLastLocations = new SparseArray<>(2);
 
         mEnabledListeners = new ArrayList<>();
+        mProviderRequestListeners = new CopyOnWriteArrayList<>();
 
         mLocationManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(LocationManagerInternal.class));
@@ -1344,6 +1349,7 @@
             // if external entities are registering listeners it's their responsibility to
             // unregister them before stopManager() is called
             Preconditions.checkState(mEnabledListeners.isEmpty());
+            mProviderRequestListeners.clear();
 
             mEnabled.clear();
             mLastLocations.clear();
@@ -1404,6 +1410,16 @@
         }
     }
 
+    /** Add a {@link IProviderRequestListener}. */
+    public void addProviderRequestListener(IProviderRequestListener listener) {
+        mProviderRequestListeners.add(listener);
+    }
+
+    /** Remove a {@link IProviderRequestListener}. */
+    public void removeProviderRequestListener(IProviderRequestListener listener) {
+        mProviderRequestListeners.remove(listener);
+    }
+
     public void setRealProvider(@Nullable AbstractLocationProvider provider) {
         synchronized (mLock) {
             Preconditions.checkState(mState != STATE_STOPPED);
@@ -1873,8 +1889,7 @@
         Preconditions.checkState(delayMs >= 0 && delayMs <= newRequest.getIntervalMillis());
 
         if (delayMs < MIN_REQUEST_DELAY_MS) {
-            mLocationEventLog.logProviderUpdateRequest(mName, newRequest);
-            mProvider.getController().setRequest(newRequest);
+            setProviderRequest(newRequest);
         } else {
             if (D) {
                 Log.d(TAG, mName + " provider delaying request update " + newRequest + " by "
@@ -1886,8 +1901,7 @@
                 public void onAlarm() {
                     synchronized (mLock) {
                         if (mDelayedRegister == this) {
-                            mLocationEventLog.logProviderUpdateRequest(mName, newRequest);
-                            mProvider.getController().setRequest(newRequest);
+                            setProviderRequest(newRequest);
                             mDelayedRegister = null;
                         }
                     }
@@ -1906,8 +1920,23 @@
             Preconditions.checkState(Thread.holdsLock(mLock));
         }
 
-        mLocationEventLog.logProviderUpdateRequest(mName, ProviderRequest.EMPTY_REQUEST);
-        mProvider.getController().setRequest(ProviderRequest.EMPTY_REQUEST);
+        setProviderRequest(ProviderRequest.EMPTY_REQUEST);
+    }
+
+    @GuardedBy("mLock")
+    private void setProviderRequest(ProviderRequest request) {
+        mLocationEventLog.logProviderUpdateRequest(mName, request);
+        mProvider.getController().setRequest(request);
+
+        FgThread.getHandler().post(() -> {
+            for (IProviderRequestListener listener : mProviderRequestListeners) {
+                try {
+                    listener.onProviderRequestChanged(mName, request);
+                } catch (RemoteException e) {
+                    mProviderRequestListeners.remove(listener);
+                }
+            }
+        });
     }
 
     @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index e2e5046..87e170a 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.app.BroadcastOptions;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -58,6 +59,16 @@
     private static final String TAG = "PendingIntentHolder";
     private static final boolean DEBUG_KEY_EVENT = MediaSessionService.DEBUG_KEY_EVENT;
     private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
+    // Filter apps regardless of the phone's locked/unlocked state.
+    private static final int PACKAGE_MANAGER_COMMON_FLAGS =
+            PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
+    /**
+     * Denotes the duration during which a media button receiver will be exempted from
+     * FGS-from-BG restriction and so will be allowed to start an FGS even if it is in the
+     * background state while it receives a media key event.
+     */
+    private static final long FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS = 10_000;
 
     private final int mUserId;
     private final PendingIntent mPendingIntent;
@@ -105,40 +116,22 @@
      * @return Can be {@code null} if pending intent was null.
      */
     public static MediaButtonReceiverHolder create(Context context, int userId,
-            PendingIntent pendingIntent) {
+            PendingIntent pendingIntent, String sessionPackageName) {
         if (pendingIntent == null) {
             return null;
         }
-        ComponentName componentName = (pendingIntent != null && pendingIntent.getIntent() != null)
-                ? pendingIntent.getIntent().getComponent() : null;
+        int componentType = getComponentType(pendingIntent);
+        ComponentName componentName = getComponentName(pendingIntent, componentType);
         if (componentName != null) {
-            // Explicit intent, where component name is in the PendingIntent.
             return new MediaButtonReceiverHolder(userId, pendingIntent, componentName,
-                    getComponentType(context, componentName));
-        }
-
-        // Implicit intent, where component name isn't in the PendingIntent. Try resolve.
-        PackageManager pm = context.getPackageManager();
-        Intent intent = pendingIntent.getIntent();
-        if ((componentName = resolveImplicitServiceIntent(pm, intent)) != null) {
-            return new MediaButtonReceiverHolder(
-                    userId, pendingIntent, componentName, COMPONENT_TYPE_SERVICE);
-        } else if ((componentName = resolveManifestDeclaredBroadcastReceiverIntent(pm, intent))
-                != null) {
-            return new MediaButtonReceiverHolder(
-                    userId, pendingIntent, componentName, COMPONENT_TYPE_BROADCAST);
-        } else if ((componentName = resolveImplicitActivityIntent(pm, intent)) != null) {
-            return new MediaButtonReceiverHolder(
-                    userId, pendingIntent, componentName, COMPONENT_TYPE_ACTIVITY);
+                    componentType);
         }
 
         // Failed to resolve target component for the pending intent. It's unlikely to be usable.
-        // However, the pending intent would be still used, just to follow the legacy behavior.
+        // However, the pending intent would be still used, so setting the package name to the
+        // package name of the session that set this pending intent.
         Log.w(TAG, "Unresolvable implicit intent is set, pi=" + pendingIntent);
-        String packageName = (pendingIntent != null && pendingIntent.getIntent() != null)
-                ? pendingIntent.getIntent().getPackage() : null;
-        return new MediaButtonReceiverHolder(userId, pendingIntent,
-                packageName != null ? packageName : "");
+        return new MediaButtonReceiverHolder(userId, pendingIntent, sessionPackageName);
     }
 
     public static MediaButtonReceiverHolder create(int userId, ComponentName broadcastReceiver) {
@@ -201,6 +194,9 @@
         // TODO: Find a way to also send PID/UID in secure way.
         mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackageName);
 
+        final BroadcastOptions options = BroadcastOptions.makeBasic();
+        options.setTemporaryAppWhitelistDuration(
+                FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS);
         if (mPendingIntent != null) {
             if (DEBUG_KEY_EVENT) {
                 Log.d(TAG, "Sending " + keyEvent + " to the last known PendingIntent "
@@ -208,7 +204,8 @@
             }
             try {
                 mPendingIntent.send(
-                        context, resultCode, mediaButtonIntent, onFinishedListener, handler);
+                        context, resultCode, mediaButtonIntent, onFinishedListener, handler,
+                        /* requiredPermission= */ null, options.toBundle());
             } catch (PendingIntent.CanceledException e) {
                 Log.w(TAG, "Error sending key event to media button receiver " + mPendingIntent, e);
                 return false;
@@ -231,7 +228,8 @@
                         break;
                     default:
                         // Legacy behavior for other cases.
-                        context.sendBroadcastAsUser(mediaButtonIntent, userHandle);
+                        context.sendBroadcastAsUser(mediaButtonIntent, userHandle,
+                                /* receiverPermission= */ null, options.toBundle());
                 }
             } catch (Exception e) {
                 Log.w(TAG, "Error sending media button to the restored intent "
@@ -269,6 +267,18 @@
                 String.valueOf(mComponentType));
     }
 
+    @ComponentType
+    private static int getComponentType(PendingIntent pendingIntent) {
+        if (pendingIntent.isBroadcast()) {
+            return COMPONENT_TYPE_BROADCAST;
+        } else if (pendingIntent.isActivity()) {
+            return COMPONENT_TYPE_ACTIVITY;
+        } else if (pendingIntent.isForegroundService() || pendingIntent.isService()) {
+            return COMPONENT_TYPE_SERVICE;
+        }
+        return COMPONENT_TYPE_INVALID;
+    }
+
     /**
      * Gets the type of the component
      *
@@ -284,9 +294,7 @@
         PackageManager pm = context.getPackageManager();
         try {
             ActivityInfo activityInfo = pm.getActivityInfo(componentName,
-                    PackageManager.MATCH_DIRECT_BOOT_AWARE
-                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                            | PackageManager.GET_ACTIVITIES);
+                    PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_ACTIVITIES);
             if (activityInfo != null) {
                 return COMPONENT_TYPE_ACTIVITY;
             }
@@ -294,9 +302,7 @@
         }
         try {
             ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
-                    PackageManager.MATCH_DIRECT_BOOT_AWARE
-                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                            | PackageManager.GET_SERVICES);
+                    PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_SERVICES);
             if (serviceInfo != null) {
                 return COMPONENT_TYPE_SERVICE;
             }
@@ -306,40 +312,29 @@
         return COMPONENT_TYPE_BROADCAST;
     }
 
-    private static ComponentName resolveImplicitServiceIntent(PackageManager pm, Intent intent) {
-        // Flag explanations.
-        // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
-        //     filter apps regardless of the phone's locked/unlocked state.
-        // - GET_SERVICES: Return service
-        return createComponentName(pm.resolveService(intent,
-                PackageManager.MATCH_DIRECT_BOOT_AWARE
-                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                        | PackageManager.GET_SERVICES));
-    }
-
-    private static ComponentName resolveManifestDeclaredBroadcastReceiverIntent(
-            PackageManager pm, Intent intent) {
-        // Flag explanations.
-        // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
-        //     filter apps regardless of the phone's locked/unlocked state.
-        List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(intent,
-                PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
-        return (resolveInfos != null && !resolveInfos.isEmpty())
-                ? createComponentName(resolveInfos.get(0)) : null;
-    }
-
-    private static ComponentName resolveImplicitActivityIntent(PackageManager pm, Intent intent) {
-        // Flag explanations.
-        // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
-        //     Filter apps regardless of the phone's locked/unlocked state.
-        // - MATCH_DEFAULT_ONLY:
-        //     Implicit intent receiver should be set as default. Only needed for activity.
-        // - GET_ACTIVITIES: Return activity
-        return createComponentName(pm.resolveActivity(intent,
-                PackageManager.MATCH_DIRECT_BOOT_AWARE
-                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                        | PackageManager.MATCH_DEFAULT_ONLY
-                        | PackageManager.GET_ACTIVITIES));
+    private static ComponentName getComponentName(PendingIntent pendingIntent, int componentType) {
+        List<ResolveInfo> resolveInfos = null;
+        switch (componentType) {
+            case COMPONENT_TYPE_ACTIVITY:
+                resolveInfos = pendingIntent.queryIntentComponents(
+                        PACKAGE_MANAGER_COMMON_FLAGS
+                                | PackageManager.MATCH_DEFAULT_ONLY /* Implicit intent receiver
+                                should be set as default. Only needed for activity. */
+                                | PackageManager.GET_ACTIVITIES);
+                break;
+            case COMPONENT_TYPE_SERVICE:
+                resolveInfos = pendingIntent.queryIntentComponents(
+                        PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_SERVICES);
+                break;
+            case COMPONENT_TYPE_BROADCAST:
+                resolveInfos = pendingIntent.queryIntentComponents(
+                        PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_RECEIVERS);
+                break;
+        }
+        if (resolveInfos != null && !resolveInfos.isEmpty()) {
+            return createComponentName(resolveInfos.get(0));
+        }
+        return null;
     }
 
     private static ComponentName createComponentName(ResolveInfo resolveInfo) {
diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
index 63618ee..7ef4924 100644
--- a/services/core/java/com/android/server/media/MediaKeyDispatcher.java
+++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
@@ -100,9 +100,12 @@
     /**
      * Implement this to customize the logic for which MediaButtonReceiver should consume a
      * dispatched key event.
-     *
-     * Note: This pending intent will have lower priority over the {@link MediaSession.Token}
+     * <p>
+     * This pending intent will have lower priority over the {@link MediaSession.Token}
      * returned from {@link #getMediaSession(KeyEvent, int, boolean)}.
+     * <p>
+     * Use a pending intent with an explicit intent; setting a pending intent with an implicit
+     * intent that cannot be resolved to a certain component name will fail.
      *
      * @return a {@link PendingIntent} instance that should receive the dispatched key event.
      */
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index ae58d4c..74111be 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -843,7 +843,8 @@
         }
 
         @Override
-        public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
+        public void setMediaButtonReceiver(PendingIntent pi, String sessionPackageName)
+                throws RemoteException {
             final long token = Binder.clearCallingIdentity();
             try {
                 if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
@@ -851,7 +852,7 @@
                     return;
                 }
                 mMediaButtonReceiverHolder =
-                        MediaButtonReceiverHolder.create(mContext, mUserId, pi);
+                        MediaButtonReceiverHolder.create(mContext, mUserId, pi, sessionPackageName);
                 mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
             } finally {
                 Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index c0381e4..6c1d399 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -2124,7 +2124,8 @@
                             uid, asSystemService);
                     if (pi != null) {
                         mediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mContext,
-                                mCurrentFullUserRecord.mFullUserId, pi);
+                                mCurrentFullUserRecord.mFullUserId, pi,
+                                /* sessionPackageName= */ "");
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b99a552..aa7da54 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1961,14 +1961,13 @@
             if (state.network != null) {
                 mNetIdToSubId.put(state.network.netId, parseSubId(state));
             }
-            if (state.networkInfo != null && state.networkInfo.isConnected()) {
-                // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
-                // in the object created here is never used and its value doesn't matter, so use
-                // NETWORK_TYPE_UNKNOWN.
-                final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
-                        true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
-                identified.put(state, ident);
-            }
+
+            // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
+            // in the object created here is never used and its value doesn't matter, so use
+            // NETWORK_TYPE_UNKNOWN.
+            final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+                    true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
+            identified.put(state, ident);
         }
 
         final ArraySet<String> newMeteredIfaces = new ArraySet<>();
@@ -2043,8 +2042,7 @@
         // One final pass to catch any metered ifaces that don't have explicitly
         // defined policies; typically Wi-Fi networks.
         for (NetworkState state : states) {
-            if (state.networkInfo != null && state.networkInfo.isConnected()
-                    && !state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+            if (!state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
                 matchingIfaces.clear();
                 collectIfaces(matchingIfaces, state);
                 for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 0ab35a9..9706bce 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -96,7 +96,6 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
-import android.net.NetworkInfo;
 import android.net.NetworkStack;
 import android.net.NetworkState;
 import android.net.NetworkStats;
@@ -1264,7 +1263,7 @@
 
     /**
      * Inspect all current {@link NetworkState} to derive mapping from {@code iface} to {@link
-     * NetworkStatsHistory}. When multiple {@link NetworkInfo} are active on a single {@code iface},
+     * NetworkStatsHistory}. When multiple networks are active on a single {@code iface},
      * they are combined under a single {@link NetworkIdentitySet}.
      */
     @GuardedBy("mStatsLock")
@@ -1294,84 +1293,82 @@
         final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled();
         final ArraySet<String> mobileIfaces = new ArraySet<>();
         for (NetworkState state : states) {
-            if (state.networkInfo.isConnected()) {
-                final boolean isMobile = isNetworkTypeMobile(state.networkInfo.getType());
-                final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
-                final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
-                        : getSubTypeForState(state);
-                final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
-                        isDefault, subType);
+            final boolean isMobile = isNetworkTypeMobile(state.legacyNetworkType);
+            final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
+            final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
+                    : getSubTypeForState(state);
+            final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+                    isDefault, subType);
 
-                // Traffic occurring on the base interface is always counted for
-                // both total usage and UID details.
-                final String baseIface = state.linkProperties.getInterfaceName();
-                if (baseIface != null) {
-                    findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
-                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
+            // Traffic occurring on the base interface is always counted for
+            // both total usage and UID details.
+            final String baseIface = state.linkProperties.getInterfaceName();
+            if (baseIface != null) {
+                findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
+                findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
 
-                    // Build a separate virtual interface for VT (Video Telephony) data usage.
-                    // Only do this when IMS is not metered, but VT is metered.
-                    // If IMS is metered, then the IMS network usage has already included VT usage.
-                    // VT is considered always metered in framework's layer. If VT is not metered
-                    // per carrier's policy, modem will report 0 usage for VT calls.
-                    if (state.networkCapabilities.hasCapability(
-                            NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
+                // Build a separate virtual interface for VT (Video Telephony) data usage.
+                // Only do this when IMS is not metered, but VT is metered.
+                // If IMS is metered, then the IMS network usage has already included VT usage.
+                // VT is considered always metered in framework's layer. If VT is not metered
+                // per carrier's policy, modem will report 0 usage for VT calls.
+                if (state.networkCapabilities.hasCapability(
+                        NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
 
-                        // Copy the identify from IMS one but mark it as metered.
-                        NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
-                                ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
-                                ident.getRoaming(), true /* metered */,
-                                true /* onDefaultNetwork */);
-                        findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
-                        findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
-                    }
-
-                    if (isMobile) {
-                        mobileIfaces.add(baseIface);
-                    }
+                    // Copy the identify from IMS one but mark it as metered.
+                    NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
+                            ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
+                            ident.getRoaming(), true /* metered */,
+                            true /* onDefaultNetwork */);
+                    findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
+                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
                 }
 
-                // Traffic occurring on stacked interfaces is usually clatd.
-                //
-                // UID stats are always counted on the stacked interface and never on the base
-                // interface, because the packets on the base interface do not actually match
-                // application sockets (they're not IPv4) and thus the app uid is not known.
-                // For receive this is obvious: packets must be translated from IPv6 to IPv4
-                // before the application socket can be found.
-                // For transmit: either they go through the clat daemon which by virtue of going
-                // through userspace strips the original socket association during the IPv4 to
-                // IPv6 translation process, or they are offloaded by eBPF, which doesn't:
-                // However, on an ebpf device the accounting is done in cgroup ebpf hooks,
-                // which don't trigger again post ebpf translation.
-                // (as such stats accounted to the clat uid are ignored)
-                //
-                // Interface stats are more complicated.
-                //
-                // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus
-                // *all* statistics are collected by iptables on the stacked v4-* interface.
-                //
-                // Additionally for ingress all packets bound for the clat IPv6 address are dropped
-                // in ip6tables raw prerouting and thus even non-offloaded packets are only
-                // accounted for on the stacked interface.
-                //
-                // For egress, packets subject to eBPF offload never appear on the base interface
-                // and only appear on the stacked interface. Thus to ensure packets increment
-                // interface stats, we must collate data from stacked interfaces. For xt_qtaguid
-                // (or non eBPF offloaded) TX they would appear on both, however egress interface
-                // accounting is explicitly bypassed for traffic from the clat uid.
-                //
-                final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
-                for (LinkProperties stackedLink : stackedLinks) {
-                    final String stackedIface = stackedLink.getInterfaceName();
-                    if (stackedIface != null) {
-                        findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
-                        findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
-                        if (isMobile) {
-                            mobileIfaces.add(stackedIface);
-                        }
+                if (isMobile) {
+                    mobileIfaces.add(baseIface);
+                }
+            }
 
-                        mStatsFactory.noteStackedIface(stackedIface, baseIface);
+            // Traffic occurring on stacked interfaces is usually clatd.
+            //
+            // UID stats are always counted on the stacked interface and never on the base
+            // interface, because the packets on the base interface do not actually match
+            // application sockets (they're not IPv4) and thus the app uid is not known.
+            // For receive this is obvious: packets must be translated from IPv6 to IPv4
+            // before the application socket can be found.
+            // For transmit: either they go through the clat daemon which by virtue of going
+            // through userspace strips the original socket association during the IPv4 to
+            // IPv6 translation process, or they are offloaded by eBPF, which doesn't:
+            // However, on an ebpf device the accounting is done in cgroup ebpf hooks,
+            // which don't trigger again post ebpf translation.
+            // (as such stats accounted to the clat uid are ignored)
+            //
+            // Interface stats are more complicated.
+            //
+            // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus
+            // *all* statistics are collected by iptables on the stacked v4-* interface.
+            //
+            // Additionally for ingress all packets bound for the clat IPv6 address are dropped
+            // in ip6tables raw prerouting and thus even non-offloaded packets are only
+            // accounted for on the stacked interface.
+            //
+            // For egress, packets subject to eBPF offload never appear on the base interface
+            // and only appear on the stacked interface. Thus to ensure packets increment
+            // interface stats, we must collate data from stacked interfaces. For xt_qtaguid
+            // (or non eBPF offloaded) TX they would appear on both, however egress interface
+            // accounting is explicitly bypassed for traffic from the clat uid.
+            //
+            final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
+            for (LinkProperties stackedLink : stackedLinks) {
+                final String stackedIface = stackedLink.getInterfaceName();
+                if (stackedIface != null) {
+                    findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
+                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
+                    if (isMobile) {
+                        mobileIfaces.add(stackedIface);
                     }
+
+                    mStatsFactory.noteStackedIface(stackedIface, baseIface);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 571b693..f7f1865 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -10104,7 +10104,9 @@
                 }
 
                 BackgroundThread.getHandler().post(() -> {
-                    if (info.isSystem || hasCompanionDevice(info)) {
+                    if (info.isSystem
+                            || hasCompanionDevice(info)
+                            || mAssistants.isServiceTokenValidLocked(info.service)) {
                         notifyNotificationChannelChanged(
                                 info, pkg, user, channel, modificationType);
                     }
diff --git a/services/core/java/com/android/server/pm/DefaultAppProvider.java b/services/core/java/com/android/server/pm/DefaultAppProvider.java
index a17967f..c18d0e9 100644
--- a/services/core/java/com/android/server/pm/DefaultAppProvider.java
+++ b/services/core/java/com/android/server/pm/DefaultAppProvider.java
@@ -41,14 +41,18 @@
 public class DefaultAppProvider {
     @NonNull
     private final Supplier<RoleManager> mRoleManagerSupplier;
+    @NonNull
+    private final Supplier<UserManagerInternal> mUserManagerInternalSupplier;
 
     /**
      * Create a new instance of this class
      *
      * @param roleManagerSupplier the supplier for {@link RoleManager}
      */
-    public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier) {
+    public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier,
+            @NonNull Supplier<UserManagerInternal> userManagerInternalSupplier) {
         mRoleManagerSupplier = roleManagerSupplier;
+        mUserManagerInternalSupplier = userManagerInternalSupplier;
     }
 
     /**
@@ -132,7 +136,8 @@
      */
     @Nullable
     public String getDefaultHome(@NonNull int userId) {
-        return getRoleHolder(RoleManager.ROLE_HOME, userId);
+        return getRoleHolder(RoleManager.ROLE_HOME,
+                mUserManagerInternalSupplier.get().getProfileParentId(userId));
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 6485c0c..d851e6c 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -401,11 +401,16 @@
                 String[] abiList = (cpuAbiOverride != null)
                         ? new String[]{cpuAbiOverride} : Build.SUPPORTED_ABIS;
 
-                // Enable gross and lame hacks for apps that are built with old
-                // SDK tools. We must scan their APKs for renderscript bitcode and
-                // not launch them if it's present. Don't bother checking on devices
-                // that don't have 64 bit support.
+                // If an app that contains RenderScript has target API level < 21, it needs to run
+                // with 32-bit ABI, and its APK file will contain a ".bc" file.
+                // If an app that contains RenderScript has target API level >= 21, it can run with
+                // either 32-bit or 64-bit ABI, and its APK file will not contain a ".bc" file.
+                // Therefore, on a device that supports both 32-bit and 64-bit ABIs, we scan the app
+                // APK to see if it has a ".bc" file. If so, we will run it with 32-bit ABI.
+                // However, if the device only supports 64-bit ABI but does not support 32-bit ABI,
+                // we will fail the installation for such an app because it won't be able to run.
                 boolean needsRenderScriptOverride = false;
+                // No need to check if the device only supports 32-bit
                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null
                         && NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
                     if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
@@ -414,7 +419,8 @@
                     } else {
                         throw new PackageManagerException(
                                 INSTALL_FAILED_CPU_ABI_INCOMPATIBLE,
-                                "Apks with renderscript are not supported on 64-bit only devices");
+                                "Apps that contain RenderScript with target API level < 21 are not "
+                                        + "supported on 64-bit only platforms");
                     }
                 }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1b444f8..965f68b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5761,8 +5761,8 @@
                 (i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
                 (i, pm) -> (IncrementalManager)
                         i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
-                (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(
-                        RoleManager.class)),
+                (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class),
+                        () -> LocalServices.getService(UserManagerInternal.class)),
                 (i, pm) -> new DisplayMetrics(),
                 (i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
                         i.getDisplayMetrics(), pm.mCacheDir,
@@ -8940,7 +8940,6 @@
 
     @Override
     public List<String> getAllPackages() {
-        enforceSystemOrRootOrShell("getAllPackages is limited to privileged callers");
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
         synchronized (mLock) {
@@ -23934,7 +23933,8 @@
                 writer.println("Domain verification status:");
                 writer.increaseIndent();
                 try {
-                    mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL);
+                    mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL,
+                            mSettings::getPackageLPr);
                 } catch (PackageManager.NameNotFoundException e) {
                     pw.println("Failure printing domain verification information");
                     Slog.e(TAG, "Failure printing domain verification information", e);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 532e78c..863e3fe5 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -40,6 +40,7 @@
 import android.content.LocusId;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.IShortcutService;
 import android.content.pm.LauncherApps;
@@ -4619,6 +4620,9 @@
                     case "verify-states": // hidden command to verify various internal states.
                         handleVerifyStates();
                         break;
+                    case "has-shortcut-access":
+                        handleHasShortcutAccess();
+                        break;
                     default:
                         return handleDefaultCommands(cmd);
                 }
@@ -4650,7 +4654,7 @@
             pw.println("[Deprecated] cmd shortcut get-default-launcher [--user USER_ID]");
             pw.println("    Show the default launcher");
             pw.println("    Note: This command is deprecated. Callers should query the default"
-                    + " launcher directly from RoleManager instead.");
+                    + " launcher from RoleManager instead.");
             pw.println();
             pw.println("cmd shortcut unload-user [--user USER_ID]");
             pw.println("    Unload a user from the memory");
@@ -4662,6 +4666,10 @@
             pw.println("cmd shortcut get-shortcuts [--user USER_ID] [--flags FLAGS] PACKAGE");
             pw.println("    Show the shortcuts for a package that match the given flags");
             pw.println();
+            pw.println("cmd shortcut has-shortcut-access [--user USER_ID] PACKAGE");
+            pw.println("    Prints \"true\" if the package can access shortcuts,"
+                    + " \"false\" otherwise");
+            pw.println();
         }
 
         private void handleResetThrottling() throws CommandException {
@@ -4706,11 +4714,24 @@
         private void handleGetDefaultLauncher() throws CommandException {
             synchronized (mLock) {
                 parseOptionsLocked(/* takeUser =*/ true);
+
+                final String defaultLauncher = getDefaultLauncher(mUserId);
+                if (defaultLauncher == null) {
+                    throw new CommandException(
+                            "Failed to get the default launcher for user " + mUserId);
+                }
+
+                // Get the class name of the component from PM to keep the old behaviour.
                 final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
-                // Default launcher from package manager.
-                final ComponentName defaultLauncher = mPackageManagerInternal
-                        .getHomeActivitiesAsUser(allHomeCandidates, getParentOrSelfUserId(mUserId));
-                getOutPrintWriter().println("Launcher: " + defaultLauncher);
+                mPackageManagerInternal.getHomeActivitiesAsUser(allHomeCandidates,
+                        getParentOrSelfUserId(mUserId));
+                for (ResolveInfo ri : allHomeCandidates) {
+                    final ComponentInfo ci = ri.getComponentInfo();
+                    if (ci.packageName.equals(defaultLauncher)) {
+                        getOutPrintWriter().println("Launcher: " + ci.getComponentName());
+                        break;
+                    }
+                }
             }
         }
 
@@ -4761,6 +4782,16 @@
                 throw new CommandException(th.getMessage() + "\n" + Log.getStackTraceString(th));
             }
         }
+
+        private void handleHasShortcutAccess() throws CommandException {
+            synchronized (mLock) {
+                parseOptionsLocked(/* takeUser =*/ true);
+                final String packageName = getNextArgRequired();
+
+                boolean shortcutAccess = hasShortcutHostPermissionInner(packageName, mUserId);
+                getOutPrintWriter().println(Boolean.toString(shortcutAccess));
+            }
+        }
     }
 
     // === Unit test support ===
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index af9978b..1925590 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -32,15 +32,30 @@
 
 import com.android.internal.util.CollectionUtils;
 import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
 import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
 import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.util.Arrays;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
 
+@SuppressWarnings("PointlessBooleanExpression")
 public class DomainVerificationDebug {
 
+    // Disable to turn off all logging. This is used to allow a "basic" set of debug flags to be
+    // enabled and checked in, without having everything be on or off.
+    public static final boolean DEBUG_ANY = false;
+
+    // Enable to turn on all logging. Requires enabling DEBUG_ANY.
+    public static final boolean DEBUG_ALL = false;
+
+    public static final boolean DEBUG_APPROVAL = DEBUG_ANY && (DEBUG_ALL || true);
+    public static final boolean DEBUG_BROADCASTS = DEBUG_ANY && (DEBUG_ALL || false);
+    public static final boolean DEBUG_PROXIES = DEBUG_ANY && (DEBUG_ALL || false);
+
     @NonNull
     private final DomainVerificationCollector mCollector;
 
@@ -50,7 +65,7 @@
 
     public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
             @Nullable @UserIdInt Integer userId,
-            @NonNull DomainVerificationService.Connection connection,
+            @NonNull Function<String, PackageSetting> pkgSettingFunction,
             @NonNull DomainVerificationStateMap<DomainVerificationPkgState> stateMap)
             throws NameNotFoundException {
         ArrayMap<String, Integer> reusedMap = new ArrayMap<>();
@@ -61,7 +76,7 @@
             for (int index = 0; index < size; index++) {
                 DomainVerificationPkgState pkgState = stateMap.valueAt(index);
                 String pkgName = pkgState.getPackageName();
-                PackageSetting pkgSetting = connection.getPackageSettingLocked(pkgName);
+                PackageSetting pkgSetting = pkgSettingFunction.apply(pkgName);
                 if (pkgSetting == null || pkgSetting.getPkg() == null) {
                     continue;
                 }
@@ -77,7 +92,7 @@
                 throw DomainVerificationUtils.throwPackageUnavailable(packageName);
             }
 
-            PackageSetting pkgSetting = connection.getPackageSettingLocked(packageName);
+            PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
             if (pkgSetting == null || pkgSetting.getPkg() == null) {
                 throw DomainVerificationUtils.throwPackageUnavailable(packageName);
             }
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 7ad275a..0474d78 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -32,15 +32,16 @@
 import android.util.TypedXmlSerializer;
 
 import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 import java.util.Set;
 import java.util.UUID;
+import java.util.function.Function;
 
 public interface DomainVerificationManagerInternal extends DomainVerificationManager {
 
@@ -191,12 +192,21 @@
     /**
      * Print the verification state and user selection state of a package.
      *
-     * @param packageName the package whose state to change, or all packages if none is specified
-     * @param userId      the specific user to print, or null to skip printing user selection
-     *                    states, supports {@link android.os.UserHandle#USER_ALL}
+     * @param packageName        the package whose state to change, or all packages if none is
+     *                           specified
+     * @param userId             the specific user to print, or null to skip printing user selection
+     *                           states, supports {@link android.os.UserHandle#USER_ALL}
+     * @param pkgSettingFunction the method by which to retrieve package data; if this is called
+     *                           from {@link com.android.server.pm.PackageManagerService}, it is
+     *                           expected to pass in the snapshot of {@link PackageSetting} objects,
+     *                           or if null is passed, the manager may decide to lock {@link
+     *                           com.android.server.pm.PackageManagerService} through {@link
+     *                           Connection#getPackageSettingLocked(String)}
      */
     void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
-            @Nullable @UserIdInt Integer userId) throws NameNotFoundException;
+            @Nullable @UserIdInt Integer userId,
+            @Nullable Function<String, PackageSetting> pkgSettingFunction)
+            throws NameNotFoundException;
 
     @NonNull
     DomainVerificationShell getShell();
@@ -225,7 +235,7 @@
             throws IllegalArgumentException, NameNotFoundException;
 
 
-    interface Connection {
+    interface Connection extends Function<String, PackageSetting> {
 
         /**
          * Notify that a settings change has been made and that eventually
@@ -249,10 +259,19 @@
          */
         void schedule(int code, @Nullable Object object);
 
+        // TODO(b/178733426): Make DomainVerificationService PMS snapshot aware so it can avoid
+        //  locking package state at all. This can be as simple as removing this method in favor of
+        //  accepting a PackageSetting function in at every method call, although should probably
+        //  be abstracted to a wrapper class.
         @Nullable
         PackageSetting getPackageSettingLocked(@NonNull String pkgName);
 
         @Nullable
         AndroidPackage getPackageLocked(@NonNull String pkgName);
+
+        @Override
+        default PackageSetting apply(@NonNull String pkgName) {
+            return getPackageSettingLocked(pkgName);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 53540c8..e24e5bb 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -64,13 +64,14 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
+import java.util.function.Function;
 
 public class DomainVerificationService extends SystemService
         implements DomainVerificationManagerInternal, DomainVerificationShell.Callback {
 
     private static final String TAG = "DomainVerificationService";
 
-    public static final boolean DEBUG_APPROVAL = true;
+    public static final boolean DEBUG_APPROVAL = DomainVerificationDebug.DEBUG_APPROVAL;
 
     /**
      * The new user preference API for verifying domains marked autoVerify=true in
@@ -541,8 +542,6 @@
                 userState.removeHosts(domains);
             }
         }
-
-        mConnection.scheduleWriteSettings();
     }
 
     @Nullable
@@ -848,6 +847,7 @@
     public void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state) {
         mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid());
         mLegacySettings.add(packageName, userId, state);
+        mConnection.scheduleWriteSettings();
     }
 
     @Override
@@ -890,9 +890,24 @@
 
     @Override
     public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
-            @Nullable @UserIdInt Integer userId) throws NameNotFoundException {
+            @Nullable Integer userId) throws NameNotFoundException {
+        // This method is only used by DomainVerificationShell, which doesn't lock PMS, so it's
+        // safe to pass mConnection directly here and lock PMS. This method is not exposed
+        // to the general system server/PMS.
+        printState(writer, packageName, userId, mConnection);
+    }
+
+    @Override
+    public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
+            @Nullable @UserIdInt Integer userId,
+            @Nullable Function<String, PackageSetting> pkgSettingFunction)
+            throws NameNotFoundException {
+        if (pkgSettingFunction == null) {
+            pkgSettingFunction = mConnection;
+        }
+
         synchronized (mLock) {
-            mDebug.printState(writer, packageName, userId, mConnection, mAttachedPkgStates);
+            mDebug.printState(writer, packageName, userId, pkgSettingFunction, mAttachedPkgStates);
         }
     }
 
@@ -1051,6 +1066,8 @@
                 }
             }
         }
+
+        mConnection.scheduleWriteSettings();
     }
 
     /**
@@ -1108,6 +1125,8 @@
                 }
             }
         }
+
+        mConnection.scheduleWriteSettings();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
index 715d8fb..09abdd0 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
@@ -23,9 +23,10 @@
 import android.util.Slog;
 
 import com.android.server.DeviceIdleInternal;
-import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
 import com.android.server.pm.verify.domain.DomainVerificationCollector;
+import com.android.server.pm.verify.domain.DomainVerificationDebug;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
 
 import java.util.Objects;
 import java.util.Set;
@@ -35,7 +36,7 @@
 
     String TAG = "DomainVerificationProxy";
 
-    boolean DEBUG_PROXIES = false;
+    boolean DEBUG_PROXIES = DomainVerificationDebug.DEBUG_PROXIES;
 
     static <ConnectionType extends DomainVerificationProxyV1.Connection
             & DomainVerificationProxyV2.Connection> DomainVerificationProxy makeProxy(
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
index eab89e98..9389e63 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
@@ -37,10 +37,11 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.verify.domain.DomainVerificationCollector;
+import com.android.server.pm.verify.domain.DomainVerificationDebug;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.util.Collections;
 import java.util.List;
@@ -52,7 +53,7 @@
 
     private static final String TAG = "DomainVerificationProxyV1";
 
-    private static final boolean DEBUG_BROADCASTS = false;
+    private static final boolean DEBUG_BROADCASTS = DomainVerificationDebug.DEBUG_BROADCASTS;
 
     @NonNull
     private final Context mContext;
@@ -152,18 +153,22 @@
 
                 UUID domainSetId = pair.first;
                 String packageName = pair.second;
-                DomainVerificationInfo set;
+                DomainVerificationInfo info;
                 try {
-                    set = mManager.getDomainVerificationInfo(packageName);
+                    info = mManager.getDomainVerificationInfo(packageName);
                 } catch (PackageManager.NameNotFoundException ignored) {
                     return true;
                 }
 
-                if (!Objects.equals(domainSetId, set.getIdentifier())) {
+                if (info == null) {
                     return true;
                 }
 
-                Set<String> successfulDomains = new ArraySet<>(set.getHostToStateMap().keySet());
+                if (!Objects.equals(domainSetId, info.getIdentifier())) {
+                    return true;
+                }
+
+                Set<String> successfulDomains = new ArraySet<>(info.getHostToStateMap().keySet());
                 successfulDomains.removeAll(response.failedDomains);
 
                 int callingUid = response.callingUid;
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
index 9fcbce2..1ef06036 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
@@ -28,6 +28,7 @@
 import android.os.UserHandle;
 import android.util.Slog;
 
+import com.android.server.pm.verify.domain.DomainVerificationDebug;
 import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
 
 import java.util.Set;
@@ -36,7 +37,7 @@
 
     private static final String TAG = "DomainVerificationProxyV2";
 
-    private static final boolean DEBUG_BROADCASTS = true;
+    private static final boolean DEBUG_BROADCASTS = DomainVerificationDebug.DEBUG_BROADCASTS;
 
     @NonNull
     private final Context mContext;
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index 03d7664..57cf986 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
@@ -150,7 +150,7 @@
         @Override
         public void resolveRotation(
                 @NonNull RotationResolverCallbackInternal callbackInternal, int proposedRotation,
-                int currentRotation, String packageName, long timeout,
+                int currentRotation, long timeout,
                 @NonNull CancellationSignal cancellationSignalInternal) {
             Objects.requireNonNull(callbackInternal);
             Objects.requireNonNull(cancellationSignalInternal);
@@ -159,7 +159,8 @@
                     final RotationResolverManagerPerUserService service = getServiceForUserLocked(
                             UserHandle.getCallingUserId());
                     service.resolveRotationLocked(callbackInternal, proposedRotation,
-                            currentRotation, packageName, timeout, cancellationSignalInternal);
+                            currentRotation, /* packageName */ "", timeout,
+                            cancellationSignalInternal);
                 } else {
                     Slog.w(TAG, "Rotation Resolver service is disabled.");
                     callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 263776c..5e681c6 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -2509,7 +2509,6 @@
         try {
             // force procstats to flush & combine old files into one store
             long lastHighWaterMark = readProcStatsHighWaterMark(section);
-            List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
 
             ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS];
             for (int i = 0; i < protoStreams.length; i++) {
@@ -2519,7 +2518,7 @@
             ProcessStats procStats = new ProcessStats(false);
             // Force processStatsService to aggregate all in-storage and in-memory data.
             long highWaterMark = processStatsService.getCommittedStatsMerged(
-                    lastHighWaterMark, section, true, statsFiles, procStats);
+                    lastHighWaterMark, section, true, null, procStats);
             procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
 
             for (int i = 0; i < protoStreams.length; i++) {
diff --git a/services/core/java/com/android/server/tracing/OWNERS b/services/core/java/com/android/server/tracing/OWNERS
new file mode 100644
index 0000000..f5de4eb
--- /dev/null
+++ b/services/core/java/com/android/server/tracing/OWNERS
@@ -0,0 +1,2 @@
+cfijalkovich@google.com
+carmenjackson@google.com
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
new file mode 100644
index 0000000..8f22748
--- /dev/null
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2020 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.tracing;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.UserHandle;
+import android.tracing.ITracingServiceProxy;
+import android.util.Log;
+
+import com.android.server.SystemService;
+
+/**
+ * TracingServiceProxy is the system_server intermediary between the Perfetto tracing daemon and the
+ * system tracing app Traceur.
+ *
+ * @hide
+ */
+public class TracingServiceProxy extends SystemService {
+    private static final String TAG = "TracingServiceProxy";
+
+    public static final String TRACING_SERVICE_PROXY_BINDER_NAME = "tracing.proxy";
+
+    private static final String TRACING_APP_PACKAGE_NAME = "com.android.traceur";
+    private static final String TRACING_APP_ACTIVITY = "com.android.traceur.StopTraceService";
+
+    // Keep this in sync with the definitions in TraceService
+    private static final String INTENT_ACTION_NOTIFY_SESSION_STOPPED =
+            "com.android.traceur.NOTIFY_SESSION_STOPPED";
+    private static final String INTENT_ACTION_NOTIFY_SESSION_STOLEN =
+            "com.android.traceur.NOTIFY_SESSION_STOLEN";
+
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+
+    private final ITracingServiceProxy.Stub mTracingServiceProxy = new ITracingServiceProxy.Stub() {
+        /**
+          * Notifies system tracing app that a tracing session has ended. If a session is repurposed
+          * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
+          * there is no buffer available to dump.
+          */
+        @Override
+        public void notifyTraceSessionEnded(boolean sessionStolen) {
+            notifyTraceur(sessionStolen);
+        }
+    };
+
+    public TracingServiceProxy(Context context) {
+        super(context);
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(TRACING_SERVICE_PROXY_BINDER_NAME, mTracingServiceProxy);
+    }
+
+    private void notifyTraceur(boolean sessionStolen) {
+        final Intent intent = new Intent();
+
+        try {
+            // Validate that Traceur is a system app.
+            PackageInfo info = mPackageManager.getPackageInfo(TRACING_APP_PACKAGE_NAME,
+                    PackageManager.MATCH_SYSTEM_ONLY);
+
+            intent.setClassName(info.packageName, TRACING_APP_ACTIVITY);
+            if (sessionStolen) {
+                intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOLEN);
+            } else {
+                intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOPPED);
+            }
+
+            try {
+                mContext.startForegroundServiceAsUser(intent, UserHandle.SYSTEM);
+            } catch (RuntimeException e) {
+                Log.e(TAG, "Failed to notifyTraceSessionEnded", e);
+            }
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Failed to locate Traceur", e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 5ec527a..3726407 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vcn;
 
+import static com.android.server.VcnManagementService.VDBG;
 
 import android.annotation.NonNull;
 import android.net.NetworkCapabilities;
@@ -225,7 +226,7 @@
 
     private void handleConfigUpdated(@NonNull VcnConfig config) {
         // TODO: Add a dump function in VcnConfig that omits PII. Until then, use hashCode()
-        Slog.v(getLogTag(), String.format("Config updated: config = %s", config.hashCode()));
+        Slog.v(getLogTag(), "Config updated: config = " + config.hashCode());
 
         mConfig = config;
 
@@ -251,17 +252,29 @@
     private void handleNetworkRequested(
             @NonNull NetworkRequest request, int score, int providerId) {
         if (score > getNetworkScore()) {
-            Slog.v(getLogTag(),
-                    "Request already satisfied by higher-scoring (" + score + ") network from "
-                            + "provider " + providerId + ": " + request);
+            if (VDBG) {
+                Slog.v(
+                        getLogTag(),
+                        "Request already satisfied by higher-scoring ("
+                                + score
+                                + ") network from "
+                                + "provider "
+                                + providerId
+                                + ": "
+                                + request);
+            }
             return;
         }
 
         // If preexisting VcnGatewayConnection(s) satisfy request, return
         for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
             if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
-                Slog.v(getLogTag(),
-                        "Request already satisfied by existing VcnGatewayConnection: " + request);
+                if (VDBG) {
+                    Slog.v(
+                            getLogTag(),
+                            "Request already satisfied by existing VcnGatewayConnection: "
+                                    + request);
+                }
                 return;
             }
         }
@@ -308,7 +321,7 @@
     }
 
     private String getLogTag() {
-        return String.format("%s [%d]", TAG, mSubscriptionGroup.hashCode());
+        return TAG + " [" + mSubscriptionGroup.hashCode() + "]";
     }
 
     /** Retrieves the network score for a VCN Network */
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 9ecdf1b..2503e81 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -75,6 +75,7 @@
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -297,9 +298,9 @@
     private static final int EVENT_SETUP_COMPLETED = 6;
 
     private static class EventSetupCompletedInfo implements EventInfo {
-        @NonNull public final ChildSessionConfiguration childSessionConfig;
+        @NonNull public final VcnChildSessionConfiguration childSessionConfig;
 
-        EventSetupCompletedInfo(@NonNull ChildSessionConfiguration childSessionConfig) {
+        EventSetupCompletedInfo(@NonNull VcnChildSessionConfiguration childSessionConfig) {
             this.childSessionConfig = Objects.requireNonNull(childSessionConfig);
         }
 
@@ -471,7 +472,7 @@
      * <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating
      * states, @Nullable otherwise.
      */
-    private ChildSessionConfiguration mChildConfig;
+    private VcnChildSessionConfiguration mChildConfig;
 
     /**
      * The active network agent.
@@ -659,7 +660,7 @@
                 new EventTransformCreatedInfo(direction, transform));
     }
 
-    private void childOpened(int token, @NonNull ChildSessionConfiguration childConfig) {
+    private void childOpened(int token, @NonNull VcnChildSessionConfiguration childConfig) {
         sendMessage(EVENT_SETUP_COMPLETED, token, new EventSetupCompletedInfo(childConfig));
     }
 
@@ -1008,7 +1009,7 @@
         protected void updateNetworkAgent(
                 @NonNull IpSecTunnelInterface tunnelIface,
                 @NonNull NetworkAgent agent,
-                @NonNull ChildSessionConfiguration childConfig) {
+                @NonNull VcnChildSessionConfiguration childConfig) {
             final NetworkCapabilities caps =
                     buildNetworkCapabilities(mConnectionConfig, mUnderlying);
             final LinkProperties lp =
@@ -1020,7 +1021,7 @@
 
         protected NetworkAgent buildNetworkAgent(
                 @NonNull IpSecTunnelInterface tunnelIface,
-                @NonNull ChildSessionConfiguration childConfig) {
+                @NonNull VcnChildSessionConfiguration childConfig) {
             final NetworkCapabilities caps =
                     buildNetworkCapabilities(mConnectionConfig, mUnderlying);
             final LinkProperties lp =
@@ -1068,15 +1069,15 @@
         protected void setupInterface(
                 int token,
                 @NonNull IpSecTunnelInterface tunnelIface,
-                @NonNull ChildSessionConfiguration childConfig) {
+                @NonNull VcnChildSessionConfiguration childConfig) {
             setupInterface(token, tunnelIface, childConfig, null);
         }
 
         protected void setupInterface(
                 int token,
                 @NonNull IpSecTunnelInterface tunnelIface,
-                @NonNull ChildSessionConfiguration childConfig,
-                @Nullable ChildSessionConfiguration oldChildConfig) {
+                @NonNull VcnChildSessionConfiguration childConfig,
+                @Nullable VcnChildSessionConfiguration oldChildConfig) {
             try {
                 final Set<LinkAddress> newAddrs =
                         new ArraySet<>(childConfig.getInternalAddresses());
@@ -1189,7 +1190,7 @@
         protected void setupInterfaceAndNetworkAgent(
                 int token,
                 @NonNull IpSecTunnelInterface tunnelIface,
-                @NonNull ChildSessionConfiguration childConfig) {
+                @NonNull VcnChildSessionConfiguration childConfig) {
             setupInterface(token, tunnelIface, childConfig);
 
             if (mNetworkAgent == null) {
@@ -1207,7 +1208,64 @@
      */
     class RetryTimeoutState extends ActiveBaseState {
         @Override
-        protected void processStateMsg(Message msg) {}
+        protected void enterState() throws Exception {
+            // Reset upon entry to ConnectedState
+            mFailedAttempts++;
+
+            if (mUnderlying == null) {
+                Slog.wtf(TAG, "Underlying network was null in retry state");
+                transitionTo(mDisconnectedState);
+            } else {
+                sendMessageDelayed(
+                        EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken, getNextRetryIntervalsMs());
+            }
+        }
+
+        @Override
+        protected void processStateMsg(Message msg) {
+            switch (msg.what) {
+                case EVENT_UNDERLYING_NETWORK_CHANGED:
+                    final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
+                    mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+                    // If new underlying is null, all networks were lost; go back to disconnected.
+                    if (mUnderlying == null) {
+                        removeMessages(EVENT_RETRY_TIMEOUT_EXPIRED);
+
+                        transitionTo(mDisconnectedState);
+                        return;
+                    } else if (oldUnderlying != null
+                            && mUnderlying.network.equals(oldUnderlying.network)) {
+                        // If the network has not changed, do nothing.
+                        return;
+                    }
+
+                    // Fallthrough
+                case EVENT_RETRY_TIMEOUT_EXPIRED:
+                    removeMessages(EVENT_RETRY_TIMEOUT_EXPIRED);
+
+                    transitionTo(mConnectingState);
+                    break;
+                case EVENT_DISCONNECT_REQUESTED:
+                    handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+                    break;
+                default:
+                    logUnhandledMessage(msg);
+                    break;
+            }
+        }
+
+        private long getNextRetryIntervalsMs() {
+            final int retryDelayIndex = mFailedAttempts - 1;
+            final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMs();
+
+            // Repeatedly use last item in retry timeout list.
+            if (retryDelayIndex >= retryIntervalsMs.length) {
+                return retryIntervalsMs[retryIntervalsMs.length - 1];
+            }
+
+            return retryIntervalsMs[retryDelayIndex];
+        }
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -1277,7 +1335,7 @@
     private static LinkProperties buildConnectedLinkProperties(
             @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
             @NonNull IpSecTunnelInterface tunnelIface,
-            @NonNull ChildSessionConfiguration childConfig) {
+            @NonNull VcnChildSessionConfiguration childConfig) {
         final LinkProperties lp = new LinkProperties();
 
         lp.setInterfaceName(tunnelIface.getInterfaceName());
@@ -1328,17 +1386,25 @@
         }
     }
 
-    private class ChildSessionCallbackImpl implements ChildSessionCallback {
+    /** Implementation of ChildSessionCallback, exposed for testing. */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public class VcnChildSessionCallback implements ChildSessionCallback {
         private final int mToken;
 
-        ChildSessionCallbackImpl(int token) {
+        VcnChildSessionCallback(int token) {
             mToken = token;
         }
 
+        /** Internal proxy method for injecting of mocked ChildSessionConfiguration */
+        @VisibleForTesting(visibility = Visibility.PRIVATE)
+        void onOpened(@NonNull VcnChildSessionConfiguration childConfig) {
+            Slog.v(TAG, "ChildOpened for token " + mToken);
+            childOpened(mToken, childConfig);
+        }
+
         @Override
         public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
-            Slog.v(TAG, "ChildOpened for token " + mToken);
-            childOpened(mToken, childConfig);
+            onOpened(new VcnChildSessionConfiguration(childConfig));
         }
 
         @Override
@@ -1421,7 +1487,7 @@
                 buildIkeParams(),
                 buildChildParams(),
                 new IkeSessionCallbackImpl(token),
-                new ChildSessionCallbackImpl(token));
+                new VcnChildSessionCallback(token));
     }
 
     /** External dependencies used by VcnGatewayConnection, for injection in tests */
@@ -1458,6 +1524,35 @@
         }
     }
 
+    /**
+     * Proxy implementation of Child Session Configuration, used for testing.
+     *
+     * <p>This wrapper allows mocking of the final, parcelable ChildSessionConfiguration object for
+     * testing purposes. This is the unfortunate result of mockito-inline (for mocking final
+     * classes) not working properly with system services & associated classes.
+     *
+     * <p>This class MUST EXCLUSIVELY be a passthrough, proxying calls directly to the actual
+     * ChildSessionConfiguration.
+     */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static class VcnChildSessionConfiguration {
+        private final ChildSessionConfiguration mChildConfig;
+
+        public VcnChildSessionConfiguration(ChildSessionConfiguration childConfig) {
+            mChildConfig = childConfig;
+        }
+
+        /** Retrieves the addresses to be used inside the tunnel. */
+        public List<LinkAddress> getInternalAddresses() {
+            return mChildConfig.getInternalAddresses();
+        }
+
+        /** Retrieves the DNS servers to be used inside the tunnel. */
+        public List<InetAddress> getInternalDnsServers() {
+            return mChildConfig.getInternalDnsServers();
+        }
+    }
+
     /** Proxy implementation of IKE session, used for testing. */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public static class VcnIkeSession {
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index fe4ea30..bfeec01 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.server.vcn;
 
+import static com.android.server.VcnManagementService.VDBG;
+
 import android.annotation.NonNull;
 import android.content.Context;
 import android.net.NetworkProvider;
@@ -83,11 +85,16 @@
 
     @Override
     public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
-        Slog.v(
-                TAG,
-                String.format(
-                        "Network requested: Request = %s, score = %d, providerId = %d",
-                        request, score, providerId));
+        if (VDBG) {
+            Slog.v(
+                    TAG,
+                    "Network requested: Request = "
+                            + request
+                            + ", score = "
+                            + score
+                            + ", providerId = "
+                            + providerId);
+        }
 
         final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId);
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f29b57f..68a2c5d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6528,7 +6528,7 @@
         // Allowing closing {@link ActivityRecord} to participate can lead to an Activity in another
         // task being started in the wrong orientation during the transition.
         if (!getDisplayContent().mClosingApps.contains(this)
-                && (isVisible() || getDisplayContent().mOpeningApps.contains(this))) {
+                && (isVisibleRequested() || getDisplayContent().mOpeningApps.contains(this))) {
             return mOrientation;
         }
 
@@ -7005,7 +7005,32 @@
                 return;
             }
         }
-        super.onConfigurationChanged(newParentConfig);
+
+        final DisplayContent display = mDisplayContent;
+        if (inPinnedWindowingMode() && attachedToProcess() && display != null) {
+            // If the PIP activity is changing to fullscreen with display orientation change, the
+            // fixed rotation will take effect that requires to send fixed rotation adjustments
+            // before the process configuration (if the process is a configuration listener of the
+            // activity). So when performing process configuration on client side, it can apply
+            // the adjustments (see WindowToken#onFixedRotationStatePrepared).
+            try {
+                app.pauseConfigurationDispatch();
+                super.onConfigurationChanged(newParentConfig);
+                if (mVisibleRequested && !inMultiWindowMode()) {
+                    final int rotation = display.rotationForActivityInDifferentOrientation(this);
+                    if (rotation != ROTATION_UNDEFINED) {
+                        app.resumeConfigurationDispatch();
+                        display.setFixedRotationLaunchingApp(this, rotation);
+                    }
+                }
+            } finally {
+                if (app.resumeConfigurationDispatch()) {
+                    app.dispatchConfiguration(app.getConfiguration());
+                }
+            }
+        } else {
+            super.onConfigurationChanged(newParentConfig);
+        }
 
         // Configuration's equality doesn't consider seq so if only seq number changes in resolved
         // override configuration. Therefore ConfigurationContainer doesn't change merged override
@@ -7014,7 +7039,6 @@
             onMergedOverrideConfigurationChanged();
         }
 
-        final DisplayContent display = mDisplayContent;
         if (display == null) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 79f8229..3456e51 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -772,6 +772,11 @@
             newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
         }
         newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT, new IntentSender(target));
+        ActivityOptions options = mRequest.activityOptions.getOptions(mRequest.intent,
+                mRequest.activityInfo,
+                mService.getProcessController(mRequest.caller),
+                mSupervisor);
+        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_ACTIVITY_OPTIONS, options.toBundle());
         heavy.updateIntentForHeavyWeightActivity(newIntent);
         newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
                 mRequest.activityInfo.packageName);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 4bcef40..f97af62 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -176,6 +176,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.hardware.power.Mode;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -343,7 +344,7 @@
     private StatusBarManagerInternal mStatusBarManagerInternal;
     @VisibleForTesting
     final ActivityTaskManagerInternal mInternal;
-    PowerManagerInternal mPowerManagerInternal;
+    private PowerManagerInternal mPowerManagerInternal;
     private UsageStatsManagerInternal mUsageStatsInternal;
 
     PendingIntentController mPendingIntentController;
@@ -591,6 +592,22 @@
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({
+            POWER_MODE_REASON_START_ACTIVITY,
+            POWER_MODE_REASON_FREEZE_DISPLAY,
+            POWER_MODE_REASON_ALL,
+    })
+    @interface PowerModeReason {}
+
+    static final int POWER_MODE_REASON_START_ACTIVITY = 1 << 0;
+    static final int POWER_MODE_REASON_FREEZE_DISPLAY = 1 << 1;
+    /** This can only be used by {@link #endLaunchPowerMode(int)}.*/
+    static final int POWER_MODE_REASON_ALL = (1 << 2) - 1;
+
+    /** The reasons to use {@link Mode#LAUNCH} power mode. */
+    private @PowerModeReason int mLaunchPowerModeReasons;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
             LAYOUT_REASON_CONFIG_CHANGED,
             LAYOUT_REASON_VISIBILITY_CHANGED,
     })
@@ -4095,6 +4112,20 @@
         return changes;
     }
 
+    void startLaunchPowerMode(@PowerModeReason int reason) {
+        if (mPowerManagerInternal == null) return;
+        mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
+        mLaunchPowerModeReasons |= reason;
+    }
+
+    void endLaunchPowerMode(@PowerModeReason int reason) {
+        if (mPowerManagerInternal == null || mLaunchPowerModeReasons == 0) return;
+        mLaunchPowerModeReasons &= ~reason;
+        if (mLaunchPowerModeReasons == 0) {
+            mPowerManagerInternal.setPowerMode(Mode.LAUNCH, false);
+        }
+    }
+
     /** @see WindowSurfacePlacer#deferLayout */
     void deferWindowLayout() {
         if (!mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index de43643..1264d0c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1755,7 +1755,7 @@
         }
 
         // End power mode launch before going sleep
-        mRootWindowContainer.endPowerModeLaunchIfNeeded();
+        mService.endLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_ALL);
 
         removeSleepTimeouts();
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ee1e64f..23eab98 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -952,7 +952,7 @@
         }
 
         final ActivityRecord activity = w.mActivityRecord;
-        if (activity != null) {
+        if (activity != null && activity.isVisibleRequested()) {
             activity.updateLetterboxSurface(w);
             final boolean updateAllDrawn = activity.updateDrawnWindowStates(w);
             if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) {
@@ -1451,7 +1451,9 @@
             }
             config = new Configuration();
             computeScreenConfiguration(config);
-        } else if (currentConfig != null) {
+        } else if (currentConfig != null
+                // If waiting for a remote rotation, don't prematurely update configuration.
+                && !mDisplayRotation.isWaitingForRemoteRotation()) {
             // No obvious action we need to take, but if our current state mismatches the
             // activity manager's, update it, disregarding font scale, which should remain set
             // to the value of the previous configuration.
@@ -1601,7 +1603,6 @@
                 && mFixedRotationLaunchingApp != mFixedRotationTransitionListener.mAnimatingRecents;
     }
 
-    @VisibleForTesting
     boolean isFixedRotationLaunchingApp(ActivityRecord r) {
         return mFixedRotationLaunchingApp == r;
     }
@@ -1830,8 +1831,6 @@
             if (w.mHasSurface && !rotateSeamlessly) {
                 ProtoLog.v(WM_DEBUG_ORIENTATION, "Set mOrientationChanging of %s", w);
                 w.setOrientationChanging(true);
-                mWmService.mRoot.mOrientationChangeComplete = false;
-                w.mLastFreezeDuration = 0;
             }
             w.mReportOrientationChanged = true;
         }, true /* traverseTopToBottom */);
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 48e4df7..b106657 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -481,12 +481,12 @@
 
         mRotation = rotation;
 
+        mDisplayContent.setLayoutNeeded();
+
         mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
         mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
                 mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);
 
-        mDisplayContent.setLayoutNeeded();
-
         if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
             // The screen rotation animation uses a screenshot to freeze the screen while windows
             // resize underneath. When we are rotating seamlessly, we allow the elements to
@@ -1499,6 +1499,21 @@
         }
 
         @Override
+        public boolean canUseRotationResolver() {
+            if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
+
+            switch (mCurrentAppOrientation) {
+                case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
+                case ActivityInfo.SCREEN_ORIENTATION_USER:
+                case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
+                case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+                case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+                    return true;
+            }
+            return false;
+        }
+
+        @Override
         public void onProposedRotationChanged(int rotation) {
             ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);
             Runnable r = mRunnableCache.get(rotation, null);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 26f0f09..4f8ea1a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -284,7 +284,8 @@
             // Just to be sure end the launch hint in case the target activity was never launched.
             // However, if we're keeping the activity and making it visible, we can leave it on.
             if (reorderMode != REORDER_KEEP_IN_PLACE) {
-                mService.mRootWindowContainer.endPowerModeLaunchIfNeeded();
+                mService.endLaunchPowerMode(
+                        ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
             }
 
             // Once the target is shown, prevent spurious background app switches
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2c97f78..ceebe95 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -89,7 +89,6 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
 
@@ -267,9 +266,6 @@
      */
     final SparseArray<SleepToken> mSleepTokens = new SparseArray<>();
 
-    /** Set when a power mode launch has started, but not ended. */
-    private boolean mPowerModeLaunchStarted;
-
     // The default minimal size that will be used if the activity doesn't specify its minimal size.
     // It will be calculated when the default display gets added.
     int mDefaultMinSizeOfResizeableTaskDp = -1;
@@ -1184,10 +1180,7 @@
             mUpdateRotation = true;
             doRequest = true;
         }
-        if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
-            mOrientationChangeComplete = false;
-        } else {
-            mOrientationChangeComplete = true;
+        if (mOrientationChangeComplete) {
             mLastWindowFreezeSource = mWmService.mAnimator.mLastWindowFreezeSource;
             if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
                 doRequest = true;
@@ -3327,7 +3320,7 @@
             }
         }
         // End power mode launch when idle.
-        endPowerModeLaunchIfNeeded();
+        mService.endLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
         return true;
     }
 
@@ -3540,17 +3533,9 @@
             sendPowerModeLaunch = noResumedActivities[0] || allFocusedProcessesDiffer[0];
         }
 
-        if (sendPowerModeLaunch && mService.mPowerManagerInternal != null) {
-            mService.mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
-            mPowerModeLaunchStarted = true;
-        }
-    }
-
-    void endPowerModeLaunchIfNeeded() {
-        // Trigger launch power mode off if activity is launched
-        if (mPowerModeLaunchStarted && mService.mPowerManagerInternal != null) {
-            mService.mPowerManagerInternal.setPowerMode(Mode.LAUNCH, false);
-            mPowerModeLaunchStarted = false;
+        if (sendPowerModeLaunch) {
+            mService.startLaunchPowerMode(
+                    ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e44a028..9bbbbe0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -810,6 +810,16 @@
     private boolean mDeferTaskAppear;
 
     /**
+     * Forces this task to be unorganized. Currently it is used for deferring the control of
+     * organizer when windowing mode is changing from PiP to fullscreen with orientation change.
+     * It is true only during Task#setWindowingMode ~ DisplayRotation#continueRotation.
+     *
+     * TODO(b/179235349): Remove this field by making surface operations from task organizer sync
+     *                    with display rotation.
+     */
+    private boolean mForceNotOrganized;
+
+    /**
      * This task was created by the task organizer which has the following implementations.
      * <ul>
      *     <lis>The task won't be removed when it is empty. Removal has to be an explicit request
@@ -2246,6 +2256,21 @@
 
         if (pipChanging) {
             mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(true);
+            // If the top activity is using fixed rotation, it should be changing from PiP to
+            // fullscreen with display orientation change. Do not notify fullscreen task organizer
+            // because the restoration of task surface and the transformation of activity surface
+            // need to be done synchronously.
+            final ActivityRecord r = topRunningActivity();
+            if (r != null && mDisplayContent.isFixedRotationLaunchingApp(r)) {
+                mForceNotOrganized = true;
+            }
+        } else if (mForceNotOrganized) {
+            // If the display orientation change is done, let the corresponding task organizer take
+            // back the control of this task.
+            final ActivityRecord r = topRunningActivity();
+            if (r == null || !mDisplayContent.isFixedRotationLaunchingApp(r)) {
+                mForceNotOrganized = false;
+            }
         }
         try {
             // We have 2 reasons why we need to report orientation change here.
@@ -2835,6 +2860,9 @@
         if (windowingMode == WINDOWING_MODE_UNDEFINED) {
             windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
         }
+        // Commit the resolved windowing mode so the canSpecifyOrientation won't get the old
+        // mode that may cause the bounds to be miscalculated, e.g. letterboxed.
+        getConfiguration().windowConfiguration.setWindowingMode(windowingMode);
         Rect outOverrideBounds =
                 getResolvedOverrideConfiguration().windowConfiguration.getBounds();
 
@@ -4533,6 +4561,9 @@
         pw.print(" mSupportsPictureInPicture="); pw.print(mSupportsPictureInPicture);
         pw.print(" isResizeable="); pw.println(isResizeable());
         pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
+        if (mForceNotOrganized) {
+            pw.print(prefix); pw.println("mForceNotOrganized=true");
+        }
         pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
     }
 
@@ -4977,6 +5008,9 @@
     }
 
     private boolean canBeOrganized() {
+        if (mForceNotOrganized) {
+            return false;
+        }
         // All root tasks can be organized
         if (isRootTask()) {
             return true;
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index e18219e..622fe05 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -497,7 +497,7 @@
     void notifyTaskDisplayChanged(int taskId, int newDisplayId) {
         final Message msg = mHandler.obtainMessage(NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG,
                 taskId, newDisplayId);
-        forAllLocalListeners(mNotifyTaskStackChanged, msg);
+        forAllLocalListeners(mNotifyTaskDisplayChanged, msg);
         msg.sendToTarget();
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 91a6664..eb32486 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -26,7 +26,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 
 import android.content.Context;
 import android.os.Trace;
@@ -134,8 +133,10 @@
         // Schedule next frame already such that back-pressure happens continuously.
         scheduleAnimation();
 
+        final RootWindowContainer root = mService.mRoot;
         mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
-        mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
+        mBulkUpdateParams = 0;
+        root.mOrientationChangeComplete = true;
         if (DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
         }
@@ -144,14 +145,14 @@
         mService.openSurfaceTransaction();
         try {
             // Remove all deferred displays, tasks, and activities.
-            mService.mRoot.handleCompleteDeferredRemoval();
+            root.handleCompleteDeferredRemoval();
 
             final AccessibilityController accessibilityController =
                     mService.mAccessibilityController;
             final int numDisplays = mDisplayContentsAnimators.size();
             for (int i = 0; i < numDisplays; i++) {
                 final int displayId = mDisplayContentsAnimators.keyAt(i);
-                final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+                final DisplayContent dc = root.getDisplayContent(displayId);
                 // Update animations of all applications, including those associated with
                 // exiting/removed apps.
                 dc.updateWindowsForAnimator();
@@ -160,7 +161,7 @@
 
             for (int i = 0; i < numDisplays; i++) {
                 final int displayId = mDisplayContentsAnimators.keyAt(i);
-                final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+                final DisplayContent dc = root.getDisplayContent(displayId);
 
                 dc.checkAppWindowsReadyToShow();
                 if (accessibilityController != null) {
@@ -179,13 +180,14 @@
             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
         }
 
-        final boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
-        final boolean doRequest = mBulkUpdateParams != 0 && mService.mRoot.copyAnimToLayoutParams();
+        final boolean hasPendingLayoutChanges = root.hasPendingLayoutChanges(this);
+        final boolean doRequest = (mBulkUpdateParams != 0 || root.mOrientationChangeComplete)
+                && root.copyAnimToLayoutParams();
         if (hasPendingLayoutChanges || doRequest) {
             mService.mWindowPlacerLocked.requestTraversal();
         }
 
-        final boolean rootAnimating = mService.mRoot.isAnimating(TRANSITION | CHILDREN /* flags */,
+        final boolean rootAnimating = root.isAnimating(TRANSITION | CHILDREN /* flags */,
                 ANIMATION_TYPE_ALL /* typesToCheck */);
         if (rootAnimating && !mLastRootAnimating) {
             Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
@@ -197,7 +199,7 @@
         mLastRootAnimating = rootAnimating;
 
         final boolean runningExpensiveAnimations =
-                mService.mRoot.isAnimating(TRANSITION | CHILDREN /* flags */,
+                root.isAnimating(TRANSITION | CHILDREN /* flags */,
                         ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_SCREEN_ROTATION
                                 | ANIMATION_TYPE_RECENTS /* typesToCheck */);
         if (runningExpensiveAnimations && !mRunningExpensiveAnimations) {
@@ -216,7 +218,7 @@
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
 
         if (mRemoveReplacedWindows) {
-            mService.mRoot.removeReplacedWindows();
+            root.removeReplacedWindows();
             mRemoveReplacedWindows = false;
         }
 
@@ -235,8 +237,8 @@
         if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
             builder.append(" UPDATE_ROTATION");
         }
-        if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
-            builder.append(" ORIENTATION_CHANGE_COMPLETE");
+        if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING) != 0) {
+            builder.append(" SET_WALLPAPER_ACTION_PENDING");
         }
         return builder.toString();
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index bb04ace..e183ea0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -611,9 +611,9 @@
     public abstract void removeNonHighRefreshRatePackage(@NonNull String packageName);
 
     /**
-     * Checks if this display is touchable.
+     * Checks if the device supports touch or faketouch.
      */
-    public abstract boolean isTouchableDisplay(int displayId);
+    public abstract boolean isTouchOrFaketouchDevice();
 
     /**
      * Returns the info associated with the input token used to determine if a key should be
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a41761f..3c7bab3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -100,6 +100,7 @@
 import static com.android.server.LockGuard.INDEX_WINDOW;
 import static com.android.server.LockGuard.installLock;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_FREEZE_DISPLAY;
 import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
 import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
@@ -750,6 +751,7 @@
     final TaskSnapshotController mTaskSnapshotController;
 
     boolean mIsTouchDevice;
+    boolean mIsFakeTouchDevice;
 
     final H mH = new H();
 
@@ -4960,6 +4962,8 @@
             mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
             mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TOUCHSCREEN);
+            mIsFakeTouchDevice = mContext.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_FAKETOUCH);
         }
 
         try {
@@ -5726,8 +5730,6 @@
         if (!w.mToken.okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
             ProtoLog.v(WM_DEBUG_ORIENTATION, "Changing surface while display frozen: %s", w);
             w.setOrientationChanging(true);
-            w.mLastFreezeDuration = 0;
-            mRoot.mOrientationChangeComplete = false;
             if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) {
                 mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
                 // XXX should probably keep timeout from
@@ -5838,6 +5840,9 @@
                             "startFreezingDisplayLocked: exitAnim=%d enterAnim=%d called by %s",
                             exitAnim, enterAnim, Debug.getCallers(8));
         mScreenFrozenLock.acquire();
+        // Apply launch power mode to reduce screen frozen time because orientation change may
+        // relaunch activity and redraw windows. This may also help speed up user switching.
+        mAtmService.startLaunchPowerMode(POWER_MODE_REASON_FREEZE_DISPLAY);
 
         mDisplayFrozen = true;
         mDisplayFreezeTime = SystemClock.elapsedRealtime();
@@ -5981,6 +5986,7 @@
         if (configChanged) {
             displayContent.sendNewConfiguration();
         }
+        mAtmService.endLaunchPowerMode(POWER_MODE_REASON_FREEZE_DISPLAY);
         mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN);
     }
 
@@ -7956,13 +7962,10 @@
         }
 
         @Override
-        public boolean isTouchableDisplay(int displayId) {
+        public boolean isTouchOrFaketouchDevice() {
             synchronized (mGlobalLock) {
-                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-                final Configuration configuration =
-                        displayContent != null ? displayContent.getConfiguration() : null;
-                return configuration != null
-                        && configuration.touchscreen == Configuration.TOUCHSCREEN_FINGER;
+                // All touchable devices are also faketouchable.
+                return mIsFakeTouchDevice;
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index da31bb2..5ef9420 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -16,25 +16,36 @@
 
 package com.android.server.wm;
 
+import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER;
+
 import static com.android.server.wm.WindowOrientationListenerProto.ENABLED;
 import static com.android.server.wm.WindowOrientationListenerProto.ROTATION;
 
+import android.app.ActivityThread;
 import android.content.Context;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.rotationresolver.RotationResolverInternal;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.Surface;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A special helper class used by the WindowManager
@@ -55,6 +66,9 @@
 
     private static final boolean USE_GRAVITY_SENSOR = false;
     private static final int DEFAULT_BATCH_LATENCY = 100000;
+    private static final int DEFAULT_ROTATION_RESOLVER_ENABLED = 0; // disabled
+    private static final String KEY_ROTATION_RESOLVER_TIMEOUT = "rotation_resolver_timeout_millis";
+    private static final long DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS = 700L;
 
     private Handler mHandler;
     private SensorManager mSensorManager;
@@ -62,7 +76,13 @@
     private int mRate;
     private String mSensorType;
     private Sensor mSensor;
-    private OrientationJudge mOrientationJudge;
+
+    @VisibleForTesting
+    OrientationJudge mOrientationJudge;
+
+    @VisibleForTesting
+    RotationResolverInternal mRotationResolverService;
+
     private int mCurrentRotation = -1;
     private final Context mContext;
     private final WindowManagerConstants mConstants;
@@ -256,6 +276,32 @@
     }
 
     /**
+     * Returns true if the current status of the phone is suitable for using rotation resolver
+     * service.
+     *
+     * To reduce the power consumption of rotation resolver service, rotation query should run less
+     * frequently than other low power orientation sensors. This method is used to check whether
+     * the current status of the phone is necessary to request a suggested screen rotation from the
+     * rotation resolver service. Note that it always returns {@code false} in the base class. It
+     * should be overridden in the derived classes.
+     */
+    public boolean canUseRotationResolver() {
+        return false;
+    }
+
+    /**
+     * Returns true if the rotation resolver feature is enabled by setting. It means {@link
+     * WindowOrientationListener} will then ask {@link RotationResolverInternal} for the appropriate
+     * screen rotation.
+     */
+    @VisibleForTesting
+    boolean isRotationResolverEnabled() {
+        return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                Settings.Secure.CAMERA_AUTOROTATE, DEFAULT_ROTATION_RESOLVER_ENABLED,
+                UserHandle.USER_CURRENT) == 1;
+    }
+
+    /**
      * Called when the rotation view of the device has changed.
      *
      * This method is called whenever the orientation becomes certain of an orientation.
@@ -1045,6 +1091,30 @@
         private int mProposedRotation = -1;
         private int mDesiredRotation = -1;
         private boolean mRotationEvaluationScheduled;
+        private long mRotationResolverTimeoutMillis;
+
+        OrientationSensorJudge() {
+            super();
+            setupRotationResolverParameters();
+        }
+
+        private void setupRotationResolverParameters() {
+            DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_WINDOW_MANAGER,
+                    ActivityThread.currentApplication().getMainExecutor(), (properties) -> {
+                        final Set<String> keys = properties.getKeyset();
+                        if (keys.contains(KEY_ROTATION_RESOLVER_TIMEOUT)) {
+                            readRotationResolverParameters();
+                        }
+                    });
+            readRotationResolverParameters();
+        }
+
+        private void readRotationResolverParameters() {
+            mRotationResolverTimeoutMillis = DeviceConfig.getLong(
+                    DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                    KEY_ROTATION_RESOLVER_TIMEOUT,
+                    DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS);
+        }
 
         @Override
         public int getProposedRotationLocked() {
@@ -1069,19 +1139,13 @@
 
         @Override
         public void onSensorChanged(SensorEvent event) {
-            int newRotation;
-
             int reportedRotation = (int) event.values[0];
             if (reportedRotation < 0 || reportedRotation > 3) {
                 return;
             }
 
-            synchronized (mLock) {
-                mDesiredRotation = reportedRotation;
-                newRotation = evaluateRotationChangeLocked();
-            }
-            if (newRotation >= 0) {
-                onProposedRotationChanged(newRotation);
+            // Log raw sensor rotation.
+            if (evaluateRotationChangeLocked() >= 0) {
                 if (mConstants.mRawSensorLoggingEnabled) {
                     FrameworkStatsLog.write(
                             FrameworkStatsLog.DEVICE_ROTATED,
@@ -1089,6 +1153,35 @@
                             rotationToLogEnum(reportedRotation));
                 }
             }
+
+            if (isRotationResolverEnabled() && canUseRotationResolver()) {
+                if (mRotationResolverService == null) {
+                    mRotationResolverService = LocalServices.getService(
+                            RotationResolverInternal.class);
+                }
+
+                final CancellationSignal cancellationSignal = new CancellationSignal();
+                mRotationResolverService.resolveRotation(
+                        new RotationResolverInternal.RotationResolverCallbackInternal() {
+                            @Override
+                            public void onSuccess(int result) {
+                                finalizeRotation(result);
+                            }
+
+                            @Override
+                            public void onFailure(int error) {
+                                finalizeRotation(reportedRotation);
+                            }
+                        },
+                        reportedRotation,
+                        mCurrentRotation,
+                        mRotationResolverTimeoutMillis,
+                        cancellationSignal);
+                getHandler().postDelayed(cancellationSignal::cancel,
+                        mRotationResolverTimeoutMillis);
+            } else {
+                finalizeRotation(reportedRotation);
+            }
         }
 
         @Override
@@ -1131,6 +1224,17 @@
             return -1;
         }
 
+        private void finalizeRotation(int reportedRotation) {
+            int newRotation;
+            synchronized (mLock) {
+                mDesiredRotation = reportedRotation;
+                newRotation = evaluateRotationChangeLocked();
+            }
+            if (newRotation >= 0) {
+                onProposedRotationChanged(newRotation);
+            }
+        }
+
         private boolean isDesiredRotationAcceptableLocked(long now) {
             if (mTouching) {
                 return false;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 8b4d415..264a3b4 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1329,6 +1329,10 @@
             mHasPendingConfigurationChange = true;
             return;
         }
+        dispatchConfiguration(config);
+    }
+
+    void dispatchConfiguration(Configuration config) {
         mHasPendingConfigurationChange = false;
         if (mThread == null) {
             if (Build.IS_DEBUGGABLE && mHasImeService) {
@@ -1367,8 +1371,13 @@
         mPauseConfigurationDispatchCount++;
     }
 
-    void resumeConfigurationDispatch() {
+    /** Returns {@code true} if the configuration change is pending to dispatch. */
+    boolean resumeConfigurationDispatch() {
+        if (mPauseConfigurationDispatchCount == 0) {
+            return false;
+        }
         mPauseConfigurationDispatchCount--;
+        return mHasPendingConfigurationChange;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c1f2d02..fd3d9ba 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1490,6 +1490,10 @@
     void setOrientationChanging(boolean changing) {
         mOrientationChanging = changing;
         mOrientationChangeTimedOut = false;
+        if (changing) {
+            mLastFreezeDuration = 0;
+            mWmService.mRoot.mOrientationChangeComplete = false;
+        }
     }
 
     void orientationChangeTimedOut() {
@@ -1721,7 +1725,7 @@
 
     @Override
     boolean isVisibleRequested() {
-        return isVisible();
+        return isVisible() && (mActivityRecord == null || mActivityRecord.isVisibleRequested());
     }
 
     /**
@@ -2132,7 +2136,8 @@
                 && !mAnimatingExit
                 && (mWindowFrames.mRelFrame.top != mWindowFrames.mLastRelFrame.top
                     || mWindowFrames.mRelFrame.left != mWindowFrames.mLastRelFrame.left)
-                && (!mIsChildWindow || !getParentWindow().hasMoved());
+                && (!mIsChildWindow || !getParentWindow().hasMoved())
+                && !mWmService.mAtmService.getTransitionController().isCollecting();
     }
 
     boolean isObscuringDisplay() {
@@ -3286,7 +3291,6 @@
             ProtoLog.v(WM_DEBUG_ORIENTATION,
                     "set mOrientationChanging of %s", this);
             setOrientationChanging(true);
-            mWmService.mRoot.mOrientationChangeComplete = false;
         }
         mLastFreezeDuration = 0;
         setDisplayLayoutNeeded();
@@ -3635,6 +3639,11 @@
         if (mActivityRecord != null && mActivityRecord.isRelaunching()) {
             return;
         }
+        // If the activity is invisible or going invisible, don't report either since it is going
+        // away. This is likely during a transition so we want to preserve the original state.
+        if (mActivityRecord != null && !mActivityRecord.isVisibleRequested()) {
+            return;
+        }
 
         if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
@@ -5304,7 +5313,7 @@
         updateSurfacePositionNonOrganized();
         // Send information to SufaceFlinger about the priority of the current window.
         updateFrameRateSelectionPriorityIfNeeded();
-        updateGlobalScaleIfNeeded();
+        if (isVisibleRequested()) updateGlobalScaleIfNeeded();
 
         mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
         super.prepareSurfaces();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 2da3dda..fe70dc1 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -52,7 +52,6 @@
 import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
 import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
 import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 
 import android.content.Context;
 import android.graphics.Matrix;
@@ -664,7 +663,7 @@
 
         if (w.getOrientationChanging()) {
             if (!w.isDrawn()) {
-                mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
+                w.mWmService.mRoot.mOrientationChangeComplete = false;
                 mAnimator.mLastWindowFreezeSource = w;
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
                         "Orientation continue waiting for draw in %s", w);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 6b9fbcb..2ee5fb0 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -43,8 +43,7 @@
     private int mLayoutRepeatCount;
 
     static final int SET_UPDATE_ROTATION                = 1 << 0;
-    static final int SET_ORIENTATION_CHANGE_COMPLETE    = 1 << 2;
-    static final int SET_WALLPAPER_ACTION_PENDING       = 1 << 3;
+    static final int SET_WALLPAPER_ACTION_PENDING       = 1 << 1;
 
     private boolean mTraversalScheduled;
     private int mDeferDepth = 0;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index c3a4609..1c3fe02 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -630,9 +630,14 @@
     void updateSurfacePosition(SurfaceControl.Transaction t) {
         super.updateSurfacePosition(t);
         if (isFixedRotationTransforming()) {
-            // The window is layouted in a simulated rotated display but the real display hasn't
-            // rotated, so here transforms its surface to fit in the real display.
-            mFixedRotationTransformState.transform(this);
+            final ActivityRecord r = asActivityRecord();
+            final Task rootTask = r != null ? r.getRootTask() : null;
+            // Don't transform the activity in PiP because the PiP task organizer will handle it.
+            if (rootTask == null || !rootTask.inPinnedWindowingMode()) {
+                // The window is laid out in a simulated rotated display but the real display hasn't
+                // rotated, so here transforms its surface to fit in the real display.
+                mFixedRotationTransformState.transform(this);
+            }
         }
     }
 
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 5b587e9..643503d 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -106,6 +106,7 @@
     jmethodID notifyFocusChanged;
     jmethodID notifySensorEvent;
     jmethodID notifySensorAccuracy;
+    jmethodID notifyVibratorState;
     jmethodID notifyUntrustedTouch;
     jmethodID filterInputEvent;
     jmethodID interceptKeyBeforeQueueing;
@@ -305,6 +306,7 @@
                            const std::vector<float>& values) override;
     void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
                               InputDeviceSensorAccuracy accuracy) override;
+    void notifyVibratorState(int32_t deviceId, bool isOn) override;
     void notifyUntrustedTouch(const std::string& obscuringPackage) override;
     bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
     void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
@@ -918,6 +920,18 @@
     checkAndClearExceptionFromCallback(env, "notifySensorAccuracy");
 }
 
+void NativeInputManager::notifyVibratorState(int32_t deviceId, bool isOn) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    ALOGD("notifyVibratorState isOn:%d", isOn);
+#endif
+    ATRACE_CALL();
+    JNIEnv* env = jniEnv();
+    ScopedLocalFrame localFrame(env);
+    env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyVibratorState,
+                        static_cast<jint>(deviceId), static_cast<jboolean>(isOn));
+    checkAndClearExceptionFromCallback(env, "notifyVibratorState");
+}
+
 void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
     ATRACE_CALL();
     JNIEnv* env = jniEnv();
@@ -2248,6 +2262,8 @@
 
     GET_METHOD_ID(gServiceClassInfo.notifySensorAccuracy, clazz, "notifySensorAccuracy", "(III)V");
 
+    GET_METHOD_ID(gServiceClassInfo.notifyVibratorState, clazz, "notifyVibratorState", "(IZ)V");
+
     GET_METHOD_ID(gServiceClassInfo.notifyUntrustedTouch, clazz, "notifyUntrustedTouch",
                   "(Ljava/lang/String;)V");
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7a4c611..98c3b99 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -186,6 +186,7 @@
 import com.android.server.testharness.TestHarnessModeService;
 import com.android.server.textclassifier.TextClassificationManagerService;
 import com.android.server.textservices.TextServicesManagerService;
+import com.android.server.tracing.TracingServiceProxy;
 import com.android.server.trust.TrustManagerService;
 import com.android.server.tv.TvInputManagerService;
 import com.android.server.tv.TvRemoteService;
@@ -2483,6 +2484,11 @@
         mSystemServiceManager.startService(AppBindingService.Lifecycle.class);
         t.traceEnd();
 
+        // Perfetto TracingServiceProxy
+        t.traceBegin("startTracingServiceProxy");
+        mSystemServiceManager.startService(TracingServiceProxy.class);
+        t.traceEnd();
+
         // It is now time to start up the app processes...
 
         t.traceBegin("MakeVibratorServiceReady");
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
new file mode 100644
index 0000000..5792e02
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.PackageUserState
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.os.Build
+import android.os.Process
+import android.util.ArraySet
+import android.util.SparseArray
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
+import com.android.server.pm.verify.domain.DomainVerificationService
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.spyThrowOnUnmocked
+import com.android.server.testutils.whenever
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mockito
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import java.io.File
+import java.util.UUID
+
+@RunWith(Parameterized::class)
+class DomainVerificationSettingsMutationTest {
+
+    companion object {
+        private const val TEST_PKG = "com.test"
+
+        // Pretend to be the system. This class doesn't verify any enforcement behavior.
+        private const val TEST_UID = Process.SYSTEM_UID
+        private const val TEST_USER_ID = 10
+        private val TEST_UUID = UUID.fromString("5168e42e-327e-432b-b562-cfb553518a70")
+
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun parameters(): Array<Any> {
+            val context: Context = mockThrowOnUnmocked {
+                whenever(
+                    enforcePermission(
+                        eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT),
+                        anyInt(), anyInt(), anyString()
+                    )
+                )
+                whenever(
+                    enforcePermission(
+                        eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
+                        anyInt(), anyInt(), anyString()
+                    )
+                )
+                whenever(
+                    enforcePermission(
+                        eq(android.Manifest.permission.INTERACT_ACROSS_USERS),
+                        anyInt(), anyInt(), anyString()
+                    )
+                )
+                whenever(
+                    enforcePermission(
+                        eq(android.Manifest.permission.SET_PREFERRED_APPLICATIONS),
+                        anyInt(), anyInt(), anyString()
+                    )
+                )
+            }
+            val proxy: DomainVerificationProxy = mockThrowOnUnmocked {
+                whenever(isCallerVerifier(anyInt())) { true }
+                whenever(sendBroadcastForPackages(any()))
+            }
+
+            val makeService: (DomainVerificationManagerInternal.Connection) -> DomainVerificationService =
+                { connection ->
+                    DomainVerificationService(
+                        context,
+                        mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
+                        mockThrowOnUnmocked {
+                            whenever(isChangeEnabled(anyLong(),any())) { true }
+                        }).apply {
+                        setConnection(connection)
+                    }
+                }
+
+            fun service(name: String, block: DomainVerificationService.() -> Unit) =
+                Params(makeService, name) { service ->
+                    service.proxy = proxy
+                    service.addPackage(mockPkgSetting())
+                    service.block()
+                }
+
+            return arrayOf(
+                service("clearPackage") {
+                    clearPackage(TEST_PKG)
+                },
+                service("clearUser") {
+                    clearUser(TEST_USER_ID)
+                },
+                service("clearState") {
+                    clearDomainVerificationState(listOf(TEST_PKG))
+                },
+                service("clearUserSelections") {
+                    clearUserSelections(listOf(TEST_PKG), TEST_USER_ID)
+                },
+                service("setStatus") {
+                    setDomainVerificationStatus(
+                        TEST_UUID,
+                        setOf("example.com"),
+                        DomainVerificationManager.STATE_SUCCESS
+                    )
+                },
+                service("setStatusInternalPackageName") {
+                    setDomainVerificationStatusInternal(
+                        TEST_PKG,
+                        DomainVerificationManager.STATE_SUCCESS,
+                        ArraySet(setOf("example.com"))
+                    )
+                },
+                service("setStatusInternalUid") {
+                    setDomainVerificationStatusInternal(
+                        TEST_UID,
+                        TEST_UUID,
+                        setOf("example.com"),
+                        DomainVerificationManager.STATE_SUCCESS
+                    )
+                },
+                service("setLinkHandlingAllowed") {
+                    setDomainVerificationLinkHandlingAllowed(TEST_PKG, true)
+                },
+                service("setLinkHandlingAllowedUserId") {
+                    setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, TEST_USER_ID)
+                },
+                service("setLinkHandlingAllowedInternal") {
+                    setDomainVerificationLinkHandlingAllowedInternal(TEST_PKG, true, TEST_USER_ID)
+                },
+                service("setUserSelection") {
+                    setDomainVerificationUserSelection(TEST_UUID, setOf("example.com"), true)
+                },
+                service("setUserSelectionUserId") {
+                    setDomainVerificationUserSelection(
+                        TEST_UUID,
+                        setOf("example.com"),
+                        true,
+                        TEST_USER_ID
+                    )
+                },
+                service("setUserSelectionInternal") {
+                    setDomainVerificationUserSelectionInternal(
+                        TEST_USER_ID,
+                        TEST_PKG,
+                        true,
+                        ArraySet(setOf("example.com")),
+                    )
+                },
+                service("setLegacyUserState") {
+                    setLegacyUserState(
+                        TEST_PKG,
+                        TEST_USER_ID,
+                        PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER
+                    )
+                },
+            )
+        }
+
+        data class Params(
+            val construct: (
+                DomainVerificationManagerInternal.Connection
+            ) -> DomainVerificationService,
+            val name: String,
+            val method: (DomainVerificationService) -> Unit
+        ) {
+            override fun toString() = name
+        }
+
+
+        fun mockPkg() = mockThrowOnUnmocked<AndroidPackage> {
+            whenever(packageName) { TEST_PKG }
+            whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+            whenever(activities) {
+                listOf(
+                    ParsedActivity().apply {
+                        addIntent(
+                            ParsedIntentInfo().apply {
+                                autoVerify = true
+                                addAction(Intent.ACTION_VIEW)
+                                addCategory(Intent.CATEGORY_BROWSABLE)
+                                addCategory(Intent.CATEGORY_DEFAULT)
+                                addDataScheme("https")
+                                addDataAuthority("example.com", null)
+                            }
+                        )
+                    }
+                )
+            }
+        }
+
+        // TODO: PackageSetting field encapsulation to move to whenever(name)
+        fun mockPkgSetting() = spyThrowOnUnmocked(
+            PackageSetting(
+                TEST_PKG,
+                TEST_PKG,
+                File("/test"),
+                null,
+                null,
+                null,
+                null,
+                1,
+                0,
+                0,
+                0,
+                null,
+                null,
+                null,
+                TEST_UUID
+            )
+        ) {
+            whenever(getPkg()) { mockPkg() }
+            whenever(domainSetId) { TEST_UUID }
+            whenever(userState) {
+                SparseArray<PackageUserState>().apply {
+                    this[0] = PackageUserState()
+                }
+            }
+        }
+    }
+
+    @Parameterized.Parameter(0)
+    lateinit var params: Params
+
+    @Test
+    fun writeScheduled() {
+        val connection = mockConnection()
+        val service = params.construct(connection)
+        params.method(service)
+
+        verify(connection).scheduleWriteSettings()
+    }
+
+    private fun mockConnection(): DomainVerificationManagerInternal.Connection =
+        mockThrowOnUnmocked {
+            whenever(callingUid) { TEST_UID }
+            whenever(callingUserId) { TEST_USER_ID }
+            whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting() }
+            whenever(getPackageLocked(TEST_PKG)) { mockPkg() }
+            whenever(schedule(anyInt(), any()))
+            whenever(scheduleWriteSettings())
+        }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index af4130d..ca53492 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -200,12 +200,12 @@
     }
 
     private static class DisplayModeWrapper {
-        public SurfaceControl.DisplayConfig config;
+        public SurfaceControl.DisplayMode mode;
         public float[] expectedAlternativeRefreshRates;
 
-        DisplayModeWrapper(SurfaceControl.DisplayConfig config,
+        DisplayModeWrapper(SurfaceControl.DisplayMode mode,
                 float[] expectedAlternativeRefreshRates) {
-            this.config = config;
+            this.mode = mode;
             this.expectedAlternativeRefreshRates = expectedAlternativeRefreshRates;
         }
     }
@@ -215,14 +215,15 @@
      * <code>expectedAlternativeRefreshRates</code> are present for each of the
      * <code>modes</code>.
      */
-    private void testAlternativeRefreshRatesCommon(FakeDisplay display, DisplayModeWrapper[] modes)
+    private void testAlternativeRefreshRatesCommon(FakeDisplay display,
+                DisplayModeWrapper[] wrappedModes)
             throws InterruptedException {
         // Update the display.
-        SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[modes.length];
-        for (int i = 0; i < modes.length; i++) {
-            configs[i] = modes[i].config;
+        SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[wrappedModes.length];
+        for (int i = 0; i < wrappedModes.length; i++) {
+            modes[i] = wrappedModes[i].mode;
         }
-        display.configs = configs;
+        display.modes = modes;
         setUpDisplay(display);
         mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
@@ -233,11 +234,11 @@
                 mListener.changedDisplays.get(mListener.changedDisplays.size() - 1);
         displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
         Display.Mode[] supportedModes = displayDevice.getDisplayDeviceInfoLocked().supportedModes;
-        assertThat(supportedModes.length).isEqualTo(configs.length);
+        assertThat(supportedModes.length).isEqualTo(modes.length);
 
-        for (int i = 0; i < modes.length; i++) {
-            assertModeIsSupported(supportedModes, configs[i],
-                    modes[i].expectedAlternativeRefreshRates);
+        for (int i = 0; i < wrappedModes.length; i++) {
+            assertModeIsSupported(supportedModes, modes[i],
+                    wrappedModes[i].expectedAlternativeRefreshRates);
         }
     }
 
@@ -251,56 +252,56 @@
 
         testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 60f, 0), new float[]{24f, 50f}),
+                        createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{24f, 50f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 50f, 0), new float[]{24f, 60f}),
+                        createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{24f, 60f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 24f, 0), new float[]{50f, 60f}),
+                        createFakeDisplayMode(1920, 1080, 24f, 0), new float[]{50f, 60f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 60f, 0), new float[]{24f, 50f}),
+                        createFakeDisplayMode(3840, 2160, 60f, 0), new float[]{24f, 50f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 50f, 0), new float[]{24f, 60f}),
+                        createFakeDisplayMode(3840, 2160, 50f, 0), new float[]{24f, 60f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 24f, 0), new float[]{50f, 60f}),
+                        createFakeDisplayMode(3840, 2160, 24f, 0), new float[]{50f, 60f}),
         });
 
         testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 60f, 0), new float[]{50f}),
+                        createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{50f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 50f, 0), new float[]{60f}),
+                        createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{60f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 24f, 1), new float[0]),
+                        createFakeDisplayMode(1920, 1080, 24f, 1), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 60f, 2), new float[0]),
+                        createFakeDisplayMode(3840, 2160, 60f, 2), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 50f, 3), new float[]{24f}),
+                        createFakeDisplayMode(3840, 2160, 50f, 3), new float[]{24f}),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 24f, 3), new float[]{50f}),
+                        createFakeDisplayMode(3840, 2160, 24f, 3), new float[]{50f}),
         });
 
         testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 60f, 0), new float[0]),
+                        createFakeDisplayMode(1920, 1080, 60f, 0), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 50f, 1), new float[0]),
+                        createFakeDisplayMode(1920, 1080, 50f, 1), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(1920, 1080, 24f, 2), new float[0]),
+                        createFakeDisplayMode(1920, 1080, 24f, 2), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 60f, 3), new float[0]),
+                        createFakeDisplayMode(3840, 2160, 60f, 3), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 50f, 4), new float[0]),
+                        createFakeDisplayMode(3840, 2160, 50f, 4), new float[0]),
                 new DisplayModeWrapper(
-                        createFakeDisplayConfig(3840, 2160, 24f, 5), new float[0]),
+                        createFakeDisplayMode(3840, 2160, 24f, 5), new float[0]),
         });
     }
 
     @Test
     public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception {
-        SurfaceControl.DisplayConfig displayConfig = createFakeDisplayConfig(1920, 1080, 60f);
-        SurfaceControl.DisplayConfig[] configs =
-                new SurfaceControl.DisplayConfig[]{displayConfig};
-        FakeDisplay display = new FakeDisplay(PORT_A, configs, 0);
+        SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(1920, 1080, 60f);
+        SurfaceControl.DisplayMode[] modes =
+                new SurfaceControl.DisplayMode[]{displayMode};
+        FakeDisplay display = new FakeDisplay(PORT_A, modes, 0);
         setUpDisplay(display);
         updateAvailableDisplays();
         mAdapter.registerLocked();
@@ -312,29 +313,29 @@
         DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(
                 0).getDisplayDeviceInfoLocked();
 
-        assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
-        assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig);
+        assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
+        assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode);
 
         Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
-        assertThat(defaultMode.matches(displayConfig.width, displayConfig.height,
-                displayConfig.refreshRate)).isTrue();
+        assertThat(defaultMode.matches(displayMode.width, displayMode.height,
+                displayMode.refreshRate)).isTrue();
 
         Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
-        assertThat(activeMode.matches(displayConfig.width, displayConfig.height,
-                displayConfig.refreshRate)).isTrue();
+        assertThat(activeMode.matches(displayMode.width, displayMode.height,
+                displayMode.refreshRate)).isTrue();
 
         // Change the display
-        SurfaceControl.DisplayConfig addedDisplayInfo = createFakeDisplayConfig(3840, 2160,
+        SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(3840, 2160,
                 60f);
-        configs = new SurfaceControl.DisplayConfig[]{displayConfig, addedDisplayInfo};
-        display.configs = configs;
-        display.activeConfig = 1;
+        modes = new SurfaceControl.DisplayMode[]{displayMode, addedDisplayInfo};
+        display.modes = modes;
+        display.activeMode = 1;
         setUpDisplay(display);
         mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
-        assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1);
-        assertThat(SurfaceControl.getDisplayConfigs(display.token).length).isEqualTo(2);
+        assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1);
+        assertThat(SurfaceControl.getDisplayModes(display.token).length).isEqualTo(2);
 
         assertThat(mListener.addedDisplays.size()).isEqualTo(1);
         assertThat(mListener.changedDisplays.size()).isEqualTo(1);
@@ -343,8 +344,8 @@
         displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
         displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
 
-        assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
-        assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig);
+        assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
+        assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode);
         assertModeIsSupported(displayDeviceInfo.supportedModes, addedDisplayInfo);
 
         activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
@@ -358,11 +359,11 @@
 
     @Test
     public void testAfterDisplayChange_ActiveModeIsUpdated() throws Exception {
-        SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[]{
-                createFakeDisplayConfig(1920, 1080, 60f),
-                createFakeDisplayConfig(1920, 1080, 50f)
+        SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{
+                createFakeDisplayMode(1920, 1080, 60f),
+                createFakeDisplayMode(1920, 1080, 50f)
         };
-        FakeDisplay display = new FakeDisplay(PORT_A, configs, /* activeConfig */ 0);
+        FakeDisplay display = new FakeDisplay(PORT_A, modes, /* activeMode */ 0);
         setUpDisplay(display);
         updateAvailableDisplays();
         mAdapter.registerLocked();
@@ -378,12 +379,12 @@
         assertThat(activeMode.matches(1920, 1080, 60f)).isTrue();
 
         // Change the display
-        display.activeConfig = 1;
+        display.activeMode = 1;
         setUpDisplay(display);
         mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
-        assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1);
+        assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1);
 
         assertThat(mListener.addedDisplays.size()).isEqualTo(1);
         assertThat(mListener.changedDisplays.size()).isEqualTo(1);
@@ -471,14 +472,14 @@
     }
 
     @Test
-    public void testDisplayChange_withStaleDesiredDisplayConfigSpecs() throws Exception {
-        SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[]{
-                createFakeDisplayConfig(1920, 1080, 60f),
-                createFakeDisplayConfig(1920, 1080, 50f)
+    public void testDisplayChange_withStaleDesiredDisplayModeSpecs() throws Exception {
+        SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{
+                createFakeDisplayMode(1920, 1080, 60f),
+                createFakeDisplayMode(1920, 1080, 50f)
         };
-        final int activeConfig = 0;
-        FakeDisplay display = new FakeDisplay(PORT_A, configs, activeConfig);
-        display.desiredDisplayConfigSpecs.defaultConfig = 1;
+        final int activeMode = 0;
+        FakeDisplay display = new FakeDisplay(PORT_A, modes, activeMode);
+        display.desiredDisplayModeSpecs.defaultMode = 1;
 
         setUpDisplay(display);
         updateAvailableDisplays();
@@ -486,12 +487,12 @@
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
         // Change the display
-        display.configs = new SurfaceControl.DisplayConfig[]{
-                createFakeDisplayConfig(1920, 1080, 60f)
+        display.modes = new SurfaceControl.DisplayMode[]{
+                createFakeDisplayMode(1920, 1080, 60f)
         };
-        // SurfaceFlinger can return a stale defaultConfig. Make sure this doesn't
+        // SurfaceFlinger can return a stale defaultMode. Make sure this doesn't
         // trigger ArrayOutOfBoundsException.
-        display.desiredDisplayConfigSpecs.defaultConfig = 1;
+        display.desiredDisplayModeSpecs.defaultMode = 1;
 
         setUpDisplay(display);
         updateAvailableDisplays();
@@ -562,13 +563,13 @@
     }
 
     private void assertModeIsSupported(Display.Mode[] supportedModes,
-            SurfaceControl.DisplayConfig mode) {
+            SurfaceControl.DisplayMode mode) {
         assertThat(Arrays.stream(supportedModes).anyMatch(
                 x -> x.matches(mode.width, mode.height, mode.refreshRate))).isTrue();
     }
 
     private void assertModeIsSupported(Display.Mode[] supportedModes,
-            SurfaceControl.DisplayConfig mode, float[] alternativeRefreshRates) {
+            SurfaceControl.DisplayMode mode, float[] alternativeRefreshRates) {
         float[] sortedAlternativeRates =
                 Arrays.copyOf(alternativeRefreshRates, alternativeRefreshRates.length);
         Arrays.sort(sortedAlternativeRates);
@@ -588,13 +589,13 @@
         public final DisplayAddress.Physical address;
         public final IBinder token = new Binder();
         public final SurfaceControl.DisplayInfo info;
-        public SurfaceControl.DisplayConfig[] configs;
-        public int activeConfig;
+        public SurfaceControl.DisplayMode[] modes;
+        public int activeMode;
         public int[] colorModes = new int[]{ Display.COLOR_MODE_DEFAULT };
         public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0],
                 1000, 1000, 0);
-        public SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs =
-                new SurfaceControl.DesiredDisplayConfigSpecs(/* defaultConfig */ 0,
+        public SurfaceControl.DesiredDisplayModeSpecs desiredDisplayModeSpecs =
+                new SurfaceControl.DesiredDisplayModeSpecs(/* defaultMode */ 0,
                     /* allowGroupSwitching */ false,
                     /* primaryRefreshRateMin */ 60.f,
                     /* primaryRefreshRateMax */ 60.f,
@@ -604,17 +605,17 @@
         private FakeDisplay(int port) {
             this.address = createDisplayAddress(port);
             this.info = createFakeDisplayInfo();
-            this.configs = new SurfaceControl.DisplayConfig[]{
-                    createFakeDisplayConfig(800, 600, 60f)
+            this.modes = new SurfaceControl.DisplayMode[]{
+                    createFakeDisplayMode(800, 600, 60f)
             };
-            this.activeConfig = 0;
+            this.activeMode = 0;
         }
 
-        private FakeDisplay(int port, SurfaceControl.DisplayConfig[] configs, int activeConfig) {
+        private FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode) {
             this.address = createDisplayAddress(port);
             this.info = createFakeDisplayInfo();
-            this.configs = configs;
-            this.activeConfig = activeConfig;
+            this.modes = modes;
+            this.activeMode = activeMode;
         }
     }
 
@@ -623,16 +624,16 @@
         doReturn(display.token).when(() ->
                 SurfaceControl.getPhysicalDisplayToken(display.address.getPhysicalDisplayId()));
         doReturn(display.info).when(() -> SurfaceControl.getDisplayInfo(display.token));
-        doReturn(display.configs).when(
-                () -> SurfaceControl.getDisplayConfigs(display.token));
-        doReturn(display.activeConfig).when(() -> SurfaceControl.getActiveConfig(display.token));
+        doReturn(display.modes).when(
+                () -> SurfaceControl.getDisplayModes(display.token));
+        doReturn(display.activeMode).when(() -> SurfaceControl.getActiveDisplayMode(display.token));
         doReturn(0).when(() -> SurfaceControl.getActiveColorMode(display.token));
         doReturn(display.colorModes).when(
                 () -> SurfaceControl.getDisplayColorModes(display.token));
         doReturn(display.hdrCapabilities).when(
                 () -> SurfaceControl.getHdrCapabilities(display.token));
-        doReturn(display.desiredDisplayConfigSpecs)
-                .when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(display.token));
+        doReturn(display.desiredDisplayModeSpecs)
+                .when(() -> SurfaceControl.getDesiredDisplayModeSpecs(display.token));
     }
 
     private void updateAvailableDisplays() {
@@ -655,21 +656,21 @@
         return info;
     }
 
-    private static SurfaceControl.DisplayConfig createFakeDisplayConfig(int width, int height,
+    private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height,
             float refreshRate) {
-        return createFakeDisplayConfig(width, height, refreshRate, 0);
+        return createFakeDisplayMode(width, height, refreshRate, 0);
     }
 
-    private static SurfaceControl.DisplayConfig createFakeDisplayConfig(int width, int height,
-            float refreshRate, int configGroup) {
-        final SurfaceControl.DisplayConfig config = new SurfaceControl.DisplayConfig();
-        config.width = width;
-        config.height = height;
-        config.refreshRate = refreshRate;
-        config.xDpi = 100;
-        config.yDpi = 100;
-        config.configGroup = configGroup;
-        return config;
+    private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height,
+            float refreshRate, int group) {
+        final SurfaceControl.DisplayMode mode = new SurfaceControl.DisplayMode();
+        mode.width = width;
+        mode.height = height;
+        mode.refreshRate = refreshRate;
+        mode.xDpi = 100;
+        mode.yDpi = 100;
+        mode.group = group;
+        return mode;
     }
 
     private static void waitForHandlerToComplete(Handler handler, long waitTimeMs)
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
new file mode 100644
index 0000000..35ac897
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2021 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.job;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManagerInternal;
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.os.UserHandle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+import com.android.server.LocalServices;
+import com.android.server.job.JobConcurrencyManager.GracePeriodObserver;
+import com.android.server.job.controllers.JobStatus;
+import com.android.server.pm.UserManagerInternal;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class JobConcurrencyManagerTest {
+    private static final int UNAVAILABLE_USER = 0;
+    private JobConcurrencyManager mJobConcurrencyManager;
+    private UserManagerInternal mUserManagerInternal;
+    private ActivityManagerInternal mActivityManagerInternal;
+    private int mNextUserId;
+    private GracePeriodObserver mGracePeriodObserver;
+    private Context mContext;
+    private Resources mResources;
+
+    @BeforeClass
+    public static void setUpOnce() {
+        LocalServices.addService(UserManagerInternal.class, mock(UserManagerInternal.class));
+        LocalServices.addService(
+                ActivityManagerInternal.class, mock(ActivityManagerInternal.class));
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        LocalServices.removeServiceForTest(UserManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+    }
+
+    @Before
+    public void setUp() {
+        final JobSchedulerService jobSchedulerService = mock(JobSchedulerService.class);
+        mContext = mock(Context.class);
+        mResources = mock(Resources.class);
+        doReturn(true).when(mResources).getBoolean(
+                R.bool.config_jobSchedulerRestrictBackgroundUser);
+        when(mContext.getResources()).thenReturn(mResources);
+        doReturn(mContext).when(jobSchedulerService).getTestableContext();
+        mJobConcurrencyManager = new JobConcurrencyManager(jobSchedulerService);
+        mGracePeriodObserver = mock(GracePeriodObserver.class);
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+        mNextUserId = 10;
+        mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver;
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_currentUser() {
+        assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createCurrentUser(false))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_currentProfile() {
+        assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createCurrentUser(true))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_primaryUser() {
+        assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createPrimaryUser(false))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_primaryProfile() {
+        assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createPrimaryUser(true))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_UnexpiredUser() {
+        assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createUnexpiredUser(false))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_UnexpiredProfile() {
+        assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createUnexpiredUser(true))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_restrictedUser() {
+        assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createRestrictedUser(false))));
+    }
+
+    @Test
+    public void testShouldRunAsFgUserJob_restrictedProfile() {
+        assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
+                createJob(createRestrictedUser(true))));
+    }
+
+    private UserInfo createCurrentUser(boolean isProfile) {
+        final UserInfo ui = createNewUser();
+        doReturn(ui.id).when(mActivityManagerInternal).getCurrentUserId();
+        return isProfile ? createNewProfile(ui) : ui;
+    }
+
+    private UserInfo createPrimaryUser(boolean isProfile) {
+        final UserInfo ui = createNewUser();
+        doReturn(true).when(ui).isPrimary();
+        return isProfile ? createNewProfile(ui) : ui;
+    }
+
+    private UserInfo createUnexpiredUser(boolean isProfile) {
+        final UserInfo ui = createNewUser();
+        doReturn(true).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
+        return isProfile ? createNewProfile(ui) : ui;
+    }
+
+    private UserInfo createRestrictedUser(boolean isProfile) {
+        final UserInfo ui = createNewUser();
+        doReturn(UNAVAILABLE_USER).when(mActivityManagerInternal).getCurrentUserId();
+        doReturn(false).when(ui).isPrimary();
+        doReturn(false).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
+        return isProfile ? createNewProfile(ui) : ui;
+    }
+
+    private UserInfo createNewProfile(UserInfo parent) {
+        final UserInfo ui = createNewUser();
+        parent.profileGroupId = parent.id;
+        ui.profileGroupId = parent.id;
+        doReturn(true).when(ui).isProfile();
+        return ui;
+    }
+
+    private UserInfo createNewUser() {
+        final UserInfo ui = mock(UserInfo.class);
+        ui.id = mNextUserId++;
+        doReturn(ui).when(mUserManagerInternal).getUserInfo(ui.id);
+        ui.profileGroupId = UserInfo.NO_PROFILE_GROUP_ID;
+        return ui;
+    }
+
+    private static JobStatus createJob(UserInfo userInfo) {
+        JobStatus jobStatus = JobStatus.createFromJobInfo(
+                new JobInfo.Builder(1, new ComponentName("foo", "bar")).build(),
+                userInfo.id * UserHandle.PER_USER_RANGE,
+                null, userInfo.id, "JobConcurrencyManagerTest");
+        return jobStatus;
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 4effa4d..f2bb47b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -62,6 +62,7 @@
 import com.android.server.PowerAllowlistInternal;
 import com.android.server.SystemServiceManager;
 import com.android.server.job.controllers.JobStatus;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.usage.AppStandbyInternal;
 
 import org.junit.After;
@@ -128,6 +129,9 @@
         // Called in DeviceIdleJobsController constructor.
         doReturn(mock(DeviceIdleInternal.class))
                 .when(() -> LocalServices.getService(DeviceIdleInternal.class));
+        // Used in JobConcurrencyManager.
+        doReturn(mock(UserManagerInternal.class))
+                .when(() -> LocalServices.getService(UserManagerInternal.class));
         // Used in JobStatus.
         doReturn(mock(PackageManagerInternal.class))
                 .when(() -> LocalServices.getService(PackageManagerInternal.class));
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 3b5cc88..54fa89a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -64,6 +64,7 @@
 import android.location.LocationManagerInternal.ProviderEnabledListener;
 import android.location.LocationRequest;
 import android.location.LocationResult;
+import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
 import android.location.provider.ProviderRequest;
 import android.location.util.identity.CallerIdentity;
@@ -662,6 +663,23 @@
     }
 
     @Test
+    public void testProviderRequestListener() throws Exception {
+        IProviderRequestListener requestListener = mock(IProviderRequestListener.class);
+        mManager.addProviderRequestListener(requestListener);
+
+        ILocationListener locationListener = createMockLocationListener();
+        LocationRequest request = new LocationRequest.Builder(1).setWorkSource(
+                WORK_SOURCE).build();
+        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, locationListener);
+
+        verify(requestListener, timeout(TIMEOUT_MS).times(1)).onProviderRequestChanged(anyString(),
+                any(ProviderRequest.class));
+
+        mManager.unregisterLocationRequest(locationListener);
+        mManager.removeProviderRequestListener(requestListener);
+    }
+
+    @Test
     public void testGetCurrentLocation() throws Exception {
         ILocationCallback listener = createMockGetCurrentLocationListener();
         LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/services/tests/servicestests/res/layout/widget_preview.xml
similarity index 74%
rename from packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
rename to services/tests/servicestests/res/layout/widget_preview.xml
index e09bf7e..137ff46 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
+++ b/services/tests/servicestests/res/layout/widget_preview.xml
@@ -14,7 +14,8 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<resources>
-    <bool name="can_use_one_handed_bouncer">true</bool>
-</resources>
+<TextView android:id="@+id/widget_preview"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:text="Widget preview" />
\ No newline at end of file
diff --git a/services/tests/servicestests/res/xml/dummy_appwidget_info.xml b/services/tests/servicestests/res/xml/dummy_appwidget_info.xml
index 6546216..72f025d 100644
--- a/services/tests/servicestests/res/xml/dummy_appwidget_info.xml
+++ b/services/tests/servicestests/res/xml/dummy_appwidget_info.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
   ~ Copyright (C) 2017 The Android Open Source Project
   ~
@@ -19,6 +20,7 @@
   android:minHeight="40dp"
   android:updatePeriodMillis="86400000"
   android:previewImage="@drawable/icon1"
+  android:previewLayout="@layout/widget_preview"
   android:resizeMode="horizontal|vertical"
   android:description="@string/widget_description"
   android:widgetCategory="home_screen">
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index 2a7905a..633957a 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -50,6 +50,7 @@
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
 import android.os.Process;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
@@ -81,6 +82,7 @@
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 /**
@@ -92,6 +94,7 @@
 @Presubmit
 public class VibratorServiceTest {
 
+    private static final int TEST_TIMEOUT_MILLIS = 1_000;
     private static final int UID = Process.ROOT_UID;
     private static final int VIBRATOR_ID = 1;
     private static final String PACKAGE_NAME = "package";
@@ -342,8 +345,8 @@
         verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
 
         // VibrationThread will start this vibration async, so wait before checking it never played.
-        Thread.sleep(10);
-        assertTrue(mVibratorProvider.getEffects().isEmpty());
+        assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
+                /* timeout= */ 20));
     }
 
     @Test
@@ -399,8 +402,8 @@
         verify(mIInputManagerMock).vibrate(eq(1), any(), any());
 
         // VibrationThread will start this vibration async, so wait before checking it never played.
-        Thread.sleep(10);
-        assertTrue(mVibratorProvider.getEffects().isEmpty());
+        assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
+                /* timeout= */ 20));
     }
 
     @Test
@@ -409,16 +412,10 @@
 
         mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
         vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait before triggering callbacks.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-
-        // Wait for callback to cancel vibration.
-        Thread.sleep(10);
-        assertFalse(service.isVibrating());
+        assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
     }
 
     @Test
@@ -427,26 +424,18 @@
 
         mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
         vibrate(service, VibrationEffect.createOneShot(1000, 100), RINGTONE_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait before triggering callbacks.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-
-        // Wait for callback to cancel vibration.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        // Settings callback is async, so wait before checking it never got cancelled.
+        assertFalse(waitUntil(s -> !s.isVibrating(), service, /* timeout= */ 20));
     }
 
     @Test
     public void vibrate_withSettingsChanged_doNotCancelVibration() throws Exception {
         VibratorService service = createService();
         vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait before triggering callbacks.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
                 Vibrator.VIBRATION_INTENSITY_MEDIUM);
@@ -454,9 +443,8 @@
         // FakeSettingsProvider don't support testing triggering ContentObserver yet.
         service.updateVibrators();
 
-        // Wait for callback to cancel vibration.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        // Settings callback is async, so wait before checking it never got cancelled.
+        assertFalse(waitUntil(s -> !s.isVibrating(), service, /* timeout= */ 20));
     }
 
     @Test
@@ -488,8 +476,8 @@
         inOrderVerifier.verify(mIInputManagerMock).vibrate(eq(2), eq(effect), any());
 
         // VibrationThread will start this vibration async, so wait before checking it never played.
-        Thread.sleep(10);
-        assertTrue(mVibratorProvider.getEffects().isEmpty());
+        assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
+                /* timeout= */ 20));
     }
 
     @Test
@@ -521,8 +509,8 @@
         verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
 
         // VibrationThread will start this vibration async, so wait before checking it never played.
-        Thread.sleep(10);
-        assertTrue(mVibratorProvider.getEffects().isEmpty());
+        assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
+                /* timeout= */ 20));
     }
 
     @Test
@@ -531,18 +519,12 @@
         VibratorService service = createService();
 
         vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait before triggering callbacks.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         // Trigger callbacks from controller.
         mTestLooper.moveTimeForward(50);
         mTestLooper.dispatchAll();
-
-        // VibrationThread needs some time to react to native callbacks and stop the vibrator.
-        Thread.sleep(10);
-        assertFalse(service.isVibrating());
+        assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
     }
 
     @Test
@@ -550,16 +532,10 @@
         VibratorService service = createService();
 
         vibrate(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait before checking.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         service.cancelVibrate(service);
-
-        // VibrationThread will stop this vibration async, so wait before checking.
-        Thread.sleep(10);
-        assertFalse(service.isVibrating());
+        assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
     }
 
     @Test
@@ -584,17 +560,13 @@
         service.registerVibratorStateListener(mVibratorStateListenerMock);
 
         vibrate(service, VibrationEffect.createOneShot(30, 100), ALARM_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait before triggering callbacks.
-        Thread.sleep(10);
-        assertTrue(service.isVibrating());
+        assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         service.unregisterVibratorStateListener(mVibratorStateListenerMock);
         // Trigger callbacks from controller.
         mTestLooper.moveTimeForward(50);
         mTestLooper.dispatchAll();
-        Thread.sleep(20);
-        assertFalse(service.isVibrating());
+        assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
 
         InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock);
         // First notification done when listener is registered.
@@ -771,4 +743,15 @@
     private void setGlobalSetting(String settingName, int value) {
         Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value);
     }
+
+    private boolean waitUntil(Predicate<VibratorService> predicate,
+            VibratorService service, long timeout) throws InterruptedException {
+        long timeoutTimestamp = SystemClock.uptimeMillis() + timeout;
+        boolean predicateResult = false;
+        while (!predicateResult && SystemClock.uptimeMillis() < timeoutTimestamp) {
+            Thread.sleep(10);
+            predicateResult = predicate.test(service);
+        }
+        return predicateResult;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 27edfd4..6963a1a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -110,8 +110,6 @@
         mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
 
         when(mMockIBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient);
-        when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn(
-                true);
 
         mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
                 COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
@@ -197,8 +195,9 @@
     }
 
     @Test
-    public void sendGesture_touchableDisplay_injectEvents()
+    public void sendGesture_touchableDevice_injectEvents()
             throws RemoteException {
+        when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(true);
         setServiceBinding(COMPONENT_NAME);
         mConnection.bindLocked();
         mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
@@ -213,10 +212,9 @@
     }
 
     @Test
-    public void sendGesture_untouchableDisplay_performGestureResultFailed()
+    public void sendGesture_untouchableDevice_performGestureResultFailed()
             throws RemoteException {
-        when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn(
-                false);
+        when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(false);
         setServiceBinding(COMPONENT_NAME);
         mConnection.bindLocked();
         mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index 96f4344..78eb2df 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.appwidget;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -44,6 +46,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.widget.RemoteViews;
 
+import com.android.frameworks.servicestests.R;
 import com.android.internal.appwidget.IAppWidgetHost;
 import com.android.server.LocalServices;
 
@@ -293,6 +296,13 @@
         }
     }
 
+    public void testGetPreviewLayout() {
+        AppWidgetProviderInfo info =
+                mManager.getInstalledProvidersForPackage(mPkgName, null).get(0);
+
+        assertThat(info.previewLayout).isEqualTo(R.layout.widget_preview);
+    }
+
     private int setupHostAndWidget() {
         List<PendingHostUpdate> updates = mService.startListening(
                 mMockHost, mPkgName, HOST_ID, new int[0]).getList();
diff --git a/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java b/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java
new file mode 100644
index 0000000..1915b8c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 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.job;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.job.JobConcurrencyManager.GracePeriodObserver;
+import com.android.server.pm.UserManagerInternal;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class GracePeriodObserverTest {
+    private GracePeriodObserver mGracePeriodObserver;
+    private UserManagerInternal mUserManagerInternal;
+    private static final int FIRST_USER = 0;
+
+    @BeforeClass
+    public static void setUpOnce() {
+        UserManagerInternal userManagerInternal = mock(UserManagerInternal.class);
+        LocalServices.addService(UserManagerInternal.class, userManagerInternal);
+        ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
+        LocalServices.addService(ActivityManagerInternal.class, activityManagerInternal);
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        LocalServices.removeServiceForTest(UserManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+    }
+
+    @Before
+    public void setUp() {
+        final Context context = ApplicationProvider.getApplicationContext();
+        JobSchedulerService.sElapsedRealtimeClock =
+                Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+        doReturn(FIRST_USER)
+                .when(LocalServices.getService(ActivityManagerInternal.class)).getCurrentUserId();
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+        doReturn(true).when(mUserManagerInternal).exists(FIRST_USER);
+        mGracePeriodObserver = new GracePeriodObserver(context);
+    }
+
+    @Test
+    public void testGracePeriod() throws RemoteException {
+        final int oldUser = FIRST_USER;
+        final int newUser = 10;
+        doReturn(true).when(mUserManagerInternal).exists(newUser);
+        mGracePeriodObserver.onUserSwitchComplete(newUser);
+        assertTrue(mGracePeriodObserver.isWithinGracePeriodForUser(oldUser));
+        JobSchedulerService.sElapsedRealtimeClock =
+                Clock.offset(JobSchedulerService.sElapsedRealtimeClock,
+                        Duration.ofMillis(mGracePeriodObserver.mGracePeriod));
+        assertFalse(mGracePeriodObserver.isWithinGracePeriodForUser(oldUser));
+    }
+
+    @Test
+    public void testCleanUp() throws RemoteException {
+        final int removedUser = FIRST_USER;
+        final int newUser = 10;
+        mGracePeriodObserver.onUserSwitchComplete(newUser);
+
+        final int sizeBefore = mGracePeriodObserver.mGracePeriodExpiration.size();
+        doReturn(false).when(mUserManagerInternal).exists(removedUser);
+
+        mGracePeriodObserver.onUserRemoved(removedUser);
+        assertEquals(sizeBefore - 1, mGracePeriodObserver.mGracePeriodExpiration.size());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
index 263cf48..353ac4b 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -16,7 +16,10 @@
 
 package com.android.server.job;
 
+import static com.android.server.job.JobConcurrencyManager.NUM_WORK_TYPES;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
 
@@ -47,6 +50,10 @@
 public class WorkCountTrackerTest {
     private static final String TAG = "WorkerCountTrackerTest";
 
+    private static final double[] EQUAL_PROBABILITY_CDF =
+            buildCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES,
+                    1.0 / NUM_WORK_TYPES);
+
     private Random mRandom;
     private WorkCountTracker mWorkCountTracker;
 
@@ -56,6 +63,47 @@
         mWorkCountTracker = new WorkCountTracker();
     }
 
+    @NonNull
+    private static double[] buildCdf(double pTop, double pEj, double pBg, double pBgUser) {
+        double[] cdf = new double[JobConcurrencyManager.NUM_WORK_TYPES];
+        double sum = 0;
+
+        sum += pTop;
+        cdf[0] = sum;
+        sum += pEj;
+        cdf[1] = sum;
+        sum += pBg;
+        cdf[2] = sum;
+        sum += pBgUser;
+        cdf[3] = sum;
+
+        if (Double.compare(1, sum) != 0) {
+            throw new IllegalArgumentException("probabilities don't sum to one: " + sum);
+        }
+        return cdf;
+    }
+
+    @JobConcurrencyManager.WorkType
+    static int getRandomWorkType(double[] cdf, double rand) {
+        for (int i = cdf.length - 1; i >= 0; --i) {
+            if (rand < cdf[i] && (i == 0 || rand > cdf[i - 1])) {
+                switch (i) {
+                    case 0:
+                        return WORK_TYPE_TOP;
+                    case 1:
+                        return WORK_TYPE_EJ;
+                    case 2:
+                        return WORK_TYPE_BG;
+                    case 3:
+                        return WORK_TYPE_BGUSER;
+                    default:
+                        throw new IllegalStateException("Unknown work type");
+                }
+            }
+        }
+        throw new IllegalStateException("Couldn't pick random work type");
+    }
+
     /**
      * Represents running and pending jobs.
      */
@@ -63,25 +111,22 @@
         public final SparseIntArray running = new SparseIntArray();
         public final SparseIntArray pending = new SparseIntArray();
 
-        public void maybeEnqueueJobs(double startRatio, double fgJobRatio) {
-            while (mRandom.nextDouble() < startRatio) {
-                if (mRandom.nextDouble() < fgJobRatio) {
-                    pending.put(WORK_TYPE_TOP, pending.get(WORK_TYPE_TOP) + 1);
-                } else {
-                    pending.put(WORK_TYPE_BG, pending.get(WORK_TYPE_BG) + 1);
-                }
+        public void maybeEnqueueJobs(double probStart, double[] typeCdf) {
+            while (mRandom.nextDouble() < probStart) {
+                final int workType = getRandomWorkType(typeCdf, mRandom.nextDouble());
+                pending.put(workType, pending.get(workType) + 1);
             }
         }
 
-        public void maybeFinishJobs(double stopRatio) {
+        public void maybeFinishJobs(double probStop) {
             for (int i = running.get(WORK_TYPE_BG); i > 0; i--) {
-                if (mRandom.nextDouble() < stopRatio) {
+                if (mRandom.nextDouble() < probStop) {
                     running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1);
                     mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
                 }
             }
             for (int i = running.get(WORK_TYPE_TOP); i > 0; i--) {
-                if (mRandom.nextDouble() < stopRatio) {
+                if (mRandom.nextDouble() < probStop) {
                     running.put(WORK_TYPE_TOP, running.get(WORK_TYPE_TOP) - 1);
                     mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
                 }
@@ -116,29 +161,27 @@
         mWorkCountTracker.onCountDone();
     }
 
-    private void startPendingJobs(Jobs jobs) {
-        while ((jobs.pending.get(WORK_TYPE_TOP) > 0
-                && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE)
-                || (jobs.pending.get(WORK_TYPE_BG) > 0
-                && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)) {
-            final boolean isStartingFg = mRandom.nextBoolean();
+    private boolean hasStartablePendingJob(Jobs jobs) {
+        for (int i = 0; i < jobs.pending.size(); ++i) {
+            if (jobs.pending.valueAt(i) > 0
+                    && mWorkCountTracker.canJobStart(jobs.pending.keyAt(i)) != WORK_TYPE_NONE) {
+                return true;
+            }
+        }
+        return false;
+    }
 
-            if (isStartingFg) {
-                if (jobs.pending.get(WORK_TYPE_TOP) > 0
-                        && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) {
-                    jobs.pending.put(WORK_TYPE_TOP, jobs.pending.get(WORK_TYPE_TOP) - 1);
-                    jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) + 1);
-                    mWorkCountTracker.stageJob(WORK_TYPE_TOP);
-                    mWorkCountTracker.onJobStarted(WORK_TYPE_TOP);
-                }
-            } else {
-                if (jobs.pending.get(WORK_TYPE_BG) > 0
-                        && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) {
-                    jobs.pending.put(WORK_TYPE_BG, jobs.pending.get(WORK_TYPE_BG) - 1);
-                    jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) + 1);
-                    mWorkCountTracker.stageJob(WORK_TYPE_BG);
-                    mWorkCountTracker.onJobStarted(WORK_TYPE_BG);
-                }
+    private void startPendingJobs(Jobs jobs) {
+        while (hasStartablePendingJob(jobs)) {
+            final int startingWorkType =
+                    getRandomWorkType(EQUAL_PROBABILITY_CDF, mRandom.nextDouble());
+
+            if (jobs.pending.get(startingWorkType) > 0
+                    && mWorkCountTracker.canJobStart(startingWorkType) != WORK_TYPE_NONE) {
+                jobs.pending.put(startingWorkType, jobs.pending.get(startingWorkType) - 1);
+                jobs.running.put(startingWorkType, jobs.running.get(startingWorkType) + 1);
+                mWorkCountTracker.stageJob(startingWorkType);
+                mWorkCountTracker.onJobStarted(startingWorkType);
             }
         }
     }
@@ -149,10 +192,10 @@
     private void checkRandom(Jobs jobs, int numTests, int totalMax,
             @NonNull List<Pair<Integer, Integer>> minLimits,
             @NonNull List<Pair<Integer, Integer>> maxLimits,
-            double startRatio, double fgJobRatio, double stopRatio) {
+            double probStart, double[] typeCdf, double probStop) {
         for (int i = 0; i < numTests; i++) {
-            jobs.maybeFinishJobs(stopRatio);
-            jobs.maybeEnqueueJobs(startRatio, fgJobRatio);
+            jobs.maybeFinishJobs(probStop);
+            jobs.maybeEnqueueJobs(probStart, typeCdf);
 
             recount(jobs, totalMax, minLimits, maxLimits);
             startPendingJobs(jobs);
@@ -178,18 +221,19 @@
      */
     @Test
     public void testRandom1() {
+        assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES);
+
         final Jobs jobs = new Jobs();
 
         final int numTests = 5000;
         final int totalMax = 6;
         final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
-        final double stopRatio = 0.1;
-        final double fgJobRatio = 0.5;
-        final double startRatio = 0.1;
+        final double probStop = 0.1;
+        final double probStart = 0.1;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                EQUAL_PROBABILITY_CDF, probStop);
     }
 
     @Test
@@ -198,14 +242,14 @@
 
         final int numTests = 5000;
         final int totalMax = 2;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final List<Pair<Integer, Integer>> minLimits = List.of();
-        final double stopRatio = 0.5;
-        final double fgJobRatio = 0.5;
-        final double startRatio = 0.5;
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(0.5, 0, 0.5, 0);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     @Test
@@ -214,14 +258,14 @@
 
         final int numTests = 5000;
         final int totalMax = 2;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
-        final double stopRatio = 0.5;
-        final double fgJobRatio = 0.5;
-        final double startRatio = 0.5;
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     @Test
@@ -230,14 +274,14 @@
 
         final int numTests = 5000;
         final int totalMax = 10;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
         final List<Pair<Integer, Integer>> minLimits = List.of();
-        final double stopRatio = 0.5;
-        final double fgJobRatio = 0.5;
-        final double startRatio = 0.5;
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     @Test
@@ -246,14 +290,14 @@
 
         final int numTests = 5000;
         final int totalMax = 6;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
-        final double stopRatio = 0.5;
-        final double fgJobRatio = 0.1;
-        final double startRatio = 0.5;
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(0.1, 0, 0.8, .1);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     @Test
@@ -262,14 +306,14 @@
 
         final int numTests = 5000;
         final int totalMax = 6;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
-        final double stopRatio = 0.5;
-        final double fgJobRatio = 0.9;
-        final double startRatio = 0.5;
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(0.9, 0, 0.1, 0);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     @Test
@@ -278,14 +322,14 @@
 
         final int numTests = 5000;
         final int totalMax = 6;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
         final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
-        final double stopRatio = 0.4;
-        final double fgJobRatio = 0.1;
-        final double startRatio = 0.5;
+        final double probStop = 0.4;
+        final double[] cdf = buildCdf(0.1, 0, 0.1, .8);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     @Test
@@ -294,14 +338,136 @@
 
         final int numTests = 5000;
         final int totalMax = 6;
-        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
-        final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
-        final double stopRatio = 0.4;
-        final double fgJobRatio = 0.9;
-        final double startRatio = 0.5;
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+        final double probStop = 0.4;
+        final double[] cdf = buildCdf(0.9, 0, 0.05, 0.05);
+        final double probStart = 0.5;
 
-        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
-                startRatio, fgJobRatio, stopRatio);
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+    }
+
+    @Test
+    public void testRandom9() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(0, 0, 0.5, 0.5);
+        final double probStart = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+    }
+
+    @Test
+    public void testRandom10() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(0, 0, 0.1, 0.9);
+        final double probStart = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+    }
+
+    @Test
+    public void testRandom11() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+        final double probStop = 0.5;
+        final double[] cdf = buildCdf(0, 0, 0.9, 0.1);
+        final double probStart = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+    }
+
+    @Test
+    public void testRandom12() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2));
+        final double probStop = 0.4;
+        final double[] cdf = buildCdf(0.5, 0.5, 0, 0);
+        final double probStart = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+    }
+
+    @Test
+    public void testRandom13() {
+        assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES);
+
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 13;
+        final List<Pair<Integer, Integer>> maxLimits = List.of(
+                Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4),
+                Pair.create(WORK_TYPE_BGUSER, 3));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1));
+        final double probStop = 0.01;
+        final double probStart = 0.99;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+                EQUAL_PROBABILITY_CDF, probStop);
+    }
+
+    @Test
+    public void testRandom14() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4));
+        final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+        final double probStop = 0.4;
+        final double[] cdf = buildCdf(.1, 0.5, 0.35, 0.05);
+        final double probStart = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+    }
+
+    @Test
+    public void testRandom15() {
+        final Jobs jobs = new Jobs();
+
+        final int numTests = 5000;
+        final int totalMax = 6;
+        final List<Pair<Integer, Integer>> maxLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4),
+                        Pair.create(WORK_TYPE_BGUSER, 1));
+        final List<Pair<Integer, Integer>> minLimits =
+                List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2));
+        final double probStop = 0.4;
+        final double[] cdf = buildCdf(0.01, 0.49, 0.1, 0.4);
+        final double probStart = 0.5;
+
+        checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
     }
 
     /** Used by the following tests */
@@ -337,7 +503,7 @@
     public void testBasic() {
         checkSimple(6,
                 /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
-                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
                 /* run */ List.of(),
                 /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
                 /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
@@ -345,7 +511,7 @@
 
         checkSimple(6,
                 /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
-                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
                 /* run */ List.of(),
                 /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10)),
                 /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
@@ -354,7 +520,7 @@
         // When there are BG jobs pending, 2 (min-BG) jobs should run.
         checkSimple(6,
                 /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
-                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
                 /* run */ List.of(),
                 /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)),
                 /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)),
@@ -385,6 +551,16 @@
 
         checkSimple(8,
                 /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+                /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
+                /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 49), Pair.create(WORK_TYPE_BG, 49)),
+                /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 4)),
+                /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 47), Pair.create(WORK_TYPE_BG, 49))
+        );
+
+
+        checkSimple(8,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
                 /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 6)),
                 /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
                 /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
@@ -407,6 +583,14 @@
                 /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
                 /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
 
+        checkSimple(8,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
+                /* run */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+                /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
+                /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
+                /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
+
         checkSimple(6,
                 /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
                 /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
@@ -415,6 +599,16 @@
                 /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
                 /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
 
+        checkSimple(8,
+                /* min */ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+                /* run */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
+                /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_EJ, 5),
+                        Pair.create(WORK_TYPE_BG, 3)),
+                /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 2)),
+                /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3),
+                        Pair.create(WORK_TYPE_BG, 3)));
+
         // This could happen if we lower the effective config due to higher memory pressure after
         // we've already started running jobs. We shouldn't stop already running jobs, but also
         // shouldn't start new ones.
@@ -425,6 +619,38 @@
                 /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
                 /* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)),
                 /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
+
+        checkSimple(6,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
+                /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10),
+                        Pair.create(WORK_TYPE_BG, 3),
+                        Pair.create(WORK_TYPE_BGUSER, 3)),
+                /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+                /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 6),
+                        Pair.create(WORK_TYPE_BG, 3),
+                        Pair.create(WORK_TYPE_BGUSER, 3)));
+
+        checkSimple(6,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 3)),
+                /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* pen */ List.of(Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 3)),
+                /* resRun */ List.of(
+                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
+                /* resPen */ List.of(
+                        Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)));
+
+        checkSimple(6,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)),
+                /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+                /* pen */ List.of(Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 3)),
+                /* resRun */ List.of(
+                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)),
+                /* resPen */ List.of(
+                        Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 2)));
     }
 
     /** Tests that the counter updates properly when jobs are stopped. */
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
index c28292f..2288a89 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
@@ -16,8 +16,11 @@
 package com.android.server.job;
 
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
 import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
 import android.annotation.NonNull;
@@ -31,11 +34,9 @@
 import com.android.server.job.JobConcurrencyManager.WorkTypeConfig;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
 import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
@@ -43,9 +44,13 @@
 public class WorkTypeConfigTest {
     private static final String KEY_MAX_TOTAL = "concurrency_max_total_test";
     private static final String KEY_MAX_TOP = "concurrency_max_top_test";
+    private static final String KEY_MAX_EJ = "concurrency_max_ej_test";
     private static final String KEY_MAX_BG = "concurrency_max_bg_test";
+    private static final String KEY_MAX_BGUSER = "concurrency_max_bguser_test";
     private static final String KEY_MIN_TOP = "concurrency_min_top_test";
+    private static final String KEY_MIN_EJ = "concurrency_min_ej_test";
     private static final String KEY_MIN_BG = "concurrency_min_bg_test";
+    private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test";
 
     @After
     public void tearDown() throws Exception {
@@ -56,43 +61,27 @@
         // DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually.
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, "", false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, "", false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, "", false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, "", false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, "", false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, "", false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, "", false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, "", false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, "", false);
     }
 
     private void check(@Nullable DeviceConfig.Properties config,
             int defaultTotal,
-            @Nullable Pair<Integer, Integer> defaultTopLimits,
-            @Nullable Pair<Integer, Integer> defaultBgLimits,
+            @NonNull List<Pair<Integer, Integer>> defaultMin,
+            @NonNull List<Pair<Integer, Integer>> defaultMax,
             boolean expectedValid, int expectedTotal,
-            @NonNull Pair<Integer, Integer> expectedTopLimits,
-            @NonNull Pair<Integer, Integer> expectedBgLimits) throws Exception {
+            @NonNull List<Pair<Integer, Integer>> expectedMinLimits,
+            @NonNull List<Pair<Integer, Integer>> expectedMaxLimits) throws Exception {
         resetConfig();
         if (config != null) {
             DeviceConfig.setProperties(config);
         }
 
-        List<Pair<Integer, Integer>> defaultMin = new ArrayList<>();
-        List<Pair<Integer, Integer>> defaultMax = new ArrayList<>();
-        Integer val;
-        if (defaultTopLimits != null) {
-            if ((val = defaultTopLimits.first) != null) {
-                defaultMin.add(Pair.create(WORK_TYPE_TOP, val));
-            }
-            if ((val = defaultTopLimits.second) != null) {
-                defaultMax.add(Pair.create(WORK_TYPE_TOP, val));
-            }
-        }
-        if (defaultBgLimits != null) {
-            if ((val = defaultBgLimits.first) != null) {
-                defaultMin.add(Pair.create(WORK_TYPE_BG, val));
-            }
-            if ((val = defaultBgLimits.second) != null) {
-                defaultMax.add(Pair.create(WORK_TYPE_BG, val));
-            }
-        }
-
         final WorkTypeConfig counts;
         try {
             counts = new WorkTypeConfig("test",
@@ -112,40 +101,126 @@
 
         counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
 
-        Assert.assertEquals(expectedTotal, counts.getMaxTotal());
-        Assert.assertEquals((int) expectedTopLimits.first, counts.getMinReserved(WORK_TYPE_TOP));
-        Assert.assertEquals((int) expectedTopLimits.second, counts.getMax(WORK_TYPE_TOP));
-        Assert.assertEquals((int) expectedBgLimits.first, counts.getMinReserved(WORK_TYPE_BG));
-        Assert.assertEquals((int) expectedBgLimits.second, counts.getMax(WORK_TYPE_BG));
+        assertEquals(expectedTotal, counts.getMaxTotal());
+        for (Pair<Integer, Integer> min : expectedMinLimits) {
+            assertEquals((int) min.second, counts.getMinReserved(min.first));
+        }
+        for (Pair<Integer, Integer> max : expectedMaxLimits) {
+            assertEquals((int) max.second, counts.getMax(max.first));
+        }
     }
 
     @Test
     public void test() throws Exception {
         // Tests with various combinations.
-        check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1),
-                /*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1));
-        check(null, /*default*/ 5, Pair.create(5, null), Pair.create(0, 0),
-                /*expected*/ true, 5, Pair.create(5, 5), Pair.create(0, 1));
-        check(null, /*default*/ 0, Pair.create(5, null), Pair.create(0, 0),
-                /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
-        check(null, /*default*/ -1, null, Pair.create(-1, -1),
-                /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
-        check(null, /*default*/ 5, null, Pair.create(5, 5),
-                /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
-        check(null, /*default*/ 6, Pair.create(1, null), Pair.create(6, 5),
-                /*expected*/ false, 6, Pair.create(1, 6), Pair.create(5, 5));
-        check(null, /*default*/ 4, null, Pair.create(6, 5),
-                /*expected*/ false, 4, Pair.create(1, 4), Pair.create(3, 4));
-        check(null, /*default*/ 5, Pair.create(4, null), Pair.create(1, 1),
-                /*expected*/ true, 5, Pair.create(4, 5), Pair.create(1, 1));
-        check(null, /*default*/ 15, null, Pair.create(15, 15),
-                /*expected*/ true, 15, Pair.create(1, 15), Pair.create(14, 15));
-        check(null, /*default*/ 16, null, Pair.create(16, 16),
-                /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
-        check(null, /*default*/ 20, null, Pair.create(20, 20),
-                /*expected*/ false, 16, Pair.create(1, 16), Pair.create(15, 16));
-        check(null, /*default*/ 20, null, Pair.create(16, 16),
-                /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
+        check(null, /*default*/ 13,
+                /* min */ List.of(),
+                /* max */ List.of(),
+                /*expected*/ true, 13,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 0),
+                        Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 13), Pair.create(WORK_TYPE_EJ, 13),
+                        Pair.create(WORK_TYPE_BG, 13), Pair.create(WORK_TYPE_BGUSER, 13)));
+        check(null, /*default*/ 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+                /*expected*/ true, 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)));
+        check(null, /*default*/ 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+                        Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(
+                        Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 1)),
+                /*expected*/ true, 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+                        Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+                        Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)));
+        check(null, /*default*/ 0,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 0)),
+                /*expected*/ false, 1,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1)));
+        check(null, /*default*/ -1,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, -1)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, -1)),
+                /*expected*/ false, 1,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1)));
+        check(null, /*default*/ 5,
+                /* min */ List.of(
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)),
+                /*expected*/ true, 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)));
+        check(null, /*default*/ 6,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+                        Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 2)),
+                /* max */ List.of(
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1)),
+                /*expected*/ false, 6,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6),
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1)));
+        check(null, /*default*/ 4,
+                /* min */ List.of(
+                        Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 6)),
+                /* max */ List.of(
+                        Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)),
+                /*expected*/ false, 4,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+                        Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 4),
+                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 4)));
+        check(null, /*default*/ 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+                /*expected*/ true, 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)));
+        check(null, /*default*/ 10,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
+                        Pair.create(WORK_TYPE_BG, 1)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+                /*expected*/ true, 10,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
+                        Pair.create(WORK_TYPE_BG, 1)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)));
+        check(null, /*default*/ 15,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 15)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 15)),
+                /*expected*/ true, 15,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 14)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 15), Pair.create(WORK_TYPE_BG, 15)));
+        check(null, /*default*/ 16,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+                /*expected*/ true, 16,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
+        check(null, /*default*/ 20,
+                /* min */ List.of(
+                        Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 10)),
+                /* max */ List.of(
+                        Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 20)),
+                /*expected*/ false, 16,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+                        Pair.create(WORK_TYPE_BG, 15), Pair.create(WORK_TYPE_BGUSER, 0)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16),
+                        Pair.create(WORK_TYPE_BG, 16), Pair.create(WORK_TYPE_BGUSER, 16)));
+        check(null, /*default*/ 20,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+                /*expected*/ true, 16,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
 
         // Test for overriding with a setting string.
         check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
@@ -153,26 +228,66 @@
                         .setInt(KEY_MAX_BG, 4)
                         .setInt(KEY_MIN_BG, 3)
                         .build(),
-                /*default*/ 9, null, Pair.create(9, 9),
-                /*expected*/ true, 5, Pair.create(1, 5), Pair.create(3, 4));
+                /*default*/ 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /* max */ List.of(
+                        Pair.create(WORK_TYPE_BG, 9), Pair.create(WORK_TYPE_BGUSER, 2)),
+                /*expected*/ true, 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+                        Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)));
         check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
                         .setInt(KEY_MAX_TOTAL, 5).build(),
-                /*default*/ 9, null, Pair.create(9, 9),
-                /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
+                /*default*/ 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /*expected*/ true, 5,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 5)));
+
         check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
                         .setInt(KEY_MAX_BG, 4).build(),
-                /*default*/ 9, null, Pair.create(9, 9),
-                /*expected*/ true, 9, Pair.create(1, 9), Pair.create(4, 4));
+                /*default*/ 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /*expected*/ true, 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 4)));
         check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
                         .setInt(KEY_MIN_BG, 3).build(),
-                /*default*/ 9, null, Pair.create(9, 9),
-                /*expected*/ true, 9, Pair.create(1, 9), Pair.create(3, 9));
+                /*default*/ 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /*expected*/ true, 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 9)));
+        check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+                        .setInt(KEY_MAX_TOTAL, 20)
+                        .setInt(KEY_MAX_EJ, 5)
+                        .setInt(KEY_MIN_EJ, 2)
+                        .setInt(KEY_MAX_BG, 16)
+                        .setInt(KEY_MIN_BG, 8)
+                        .build(),
+                /*default*/ 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /*expected*/ true, 16,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 2),
+                        Pair.create(WORK_TYPE_BG, 8)),
+                /* max */
+                List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_EJ, 5),
+                        Pair.create(WORK_TYPE_BG, 16)));
+
         check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
                         .setInt(KEY_MAX_TOTAL, 20)
                         .setInt(KEY_MAX_BG, 20)
                         .setInt(KEY_MIN_BG, 8)
                         .build(),
-                /*default*/ 9, null, Pair.create(9, 9),
-                /*expected*/ true, 16, Pair.create(1, 16), Pair.create(8, 16));
+                /*default*/ 9,
+                /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+                /*expected*/ true, 16,
+                /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 8)),
+                /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 58ba907..3ebe4ef 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -19,11 +19,13 @@
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.NETWORK_STACK;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
 import static android.net.INetd.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -112,8 +114,6 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkState;
@@ -1985,13 +1985,6 @@
         return users;
     }
 
-    private NetworkInfo buildNetworkInfo() {
-        final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_MOBILE,
-                TelephonyManager.NETWORK_TYPE_LTE, null, null);
-        ni.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
-        return ni;
-    }
-
     private LinkProperties buildLinkProperties(String iface) {
         final LinkProperties lp = new LinkProperties();
         lp.setInterfaceName(iface);
@@ -2045,13 +2038,12 @@
     }
 
     private static NetworkState buildWifi() {
-        final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
-        info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
         final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+        networkCapabilities.addTransportType(TRANSPORT_WIFI);
         networkCapabilities.setSSID(TEST_SSID);
-        return new NetworkState(info, prop, networkCapabilities, null, null, TEST_SSID);
+        return new NetworkState(TYPE_WIFI, prop, networkCapabilities, null, null, TEST_SSID);
     }
 
     private void expectHasInternetPermission(int uid, boolean hasIt) throws Exception {
@@ -2072,7 +2064,7 @@
         when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
                 .thenReturn(mCarrierConfig);
         when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
-                new NetworkState(buildNetworkInfo(),
+                new NetworkState(TYPE_MOBILE,
                         buildLinkProperties(TEST_IFACE),
                         buildNetworkCapabilities(TEST_SUB_ID, roaming),
                         new Network(TEST_NET_ID), TEST_IMSI, null)
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index 62b6c71..b21b049 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -221,11 +221,13 @@
 
     // This command is deprecated. Will remove the test later.
     public void testLauncherCommands() throws Exception {
+        prepareGetRoleHoldersAsUser(getSystemLauncher().activityInfo.packageName, USER_0);
         prepareGetHomeActivitiesAsUser(
                 /* preferred */ getSystemLauncher().activityInfo.getComponentName(),
                 list(getSystemLauncher(), getFallbackLauncher()),
                 USER_0);
 
+        prepareGetRoleHoldersAsUser(CALLING_PACKAGE_2, USER_10);
         prepareGetHomeActivitiesAsUser(
                 /* preferred */ cn(CALLING_PACKAGE_2, "name"),
                 list(getSystemLauncher(), getFallbackLauncher(),
@@ -247,6 +249,7 @@
                 "Launcher: ComponentInfo{com.android.test.2/name}");
 
         // Change user-0's launcher.
+        prepareGetRoleHoldersAsUser(CALLING_PACKAGE_1, USER_0);
         prepareGetHomeActivitiesAsUser(
                 /* preferred */ cn(CALLING_PACKAGE_1, "name"),
                 list(ri(CALLING_PACKAGE_1, "name", false, 0)),
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index d876b05..7d20879 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -229,9 +229,9 @@
                 .compose();
         VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
 
-        Thread.sleep(20);
+        assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread,
+                TEST_TIMEOUT_MILLIS));
         assertTrue(vibrationThread.isAlive());
-        assertTrue(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -254,9 +254,9 @@
         VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0);
         VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
 
-        Thread.sleep(20);
+        assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread,
+                TEST_TIMEOUT_MILLIS));
         assertTrue(vibrationThread.isAlive());
-        assertTrue(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -621,10 +621,8 @@
                 .combine();
         VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
 
-        Thread.sleep(10);
-        assertTrue(vibrationThread.isAlive());
-        assertTrue(vibrationThread.getVibrators().get(1).isVibrating());
-        assertTrue(vibrationThread.getVibrators().get(2).isVibrating());
+        assertTrue(waitUntil(t -> t.getVibrators().get(2).isVibrating(), vibrationThread,
+                TEST_TIMEOUT_MILLIS));
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
new file mode 100644
index 0000000..f5d0ca7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 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 com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorManager;
+import android.hardware.input.InputSensorInfo;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.rotationresolver.RotationResolverInternal;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link com.android.server.wm.WindowOrientationListener}
+ */
+public class WindowOrientationListenerTest {
+
+    @Mock
+    private Context mMockContext;
+    @Mock
+    private Handler mMockHandler;
+    @Mock
+    private InputSensorInfo mMockInputSensorInfo;
+    @Mock
+    private SensorManager mMockSensorManager;
+    @Mock
+    private WindowManagerService mMockWindowManagerService;
+
+    private TestableRotationResolver mFakeRotationResolverInternal;
+    private com.android.server.wm.WindowOrientationListener mWindowOrientationListener;
+    private int mFinalizedRotation;
+    private boolean mRotationResolverEnabled;
+    private boolean mCanUseRotationResolver;
+    private SensorEvent mFakeSensorEvent;
+    private Sensor mFakeSensor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mRotationResolverEnabled = true;
+        mCanUseRotationResolver = true;
+
+        mFakeRotationResolverInternal = new TestableRotationResolver();
+        doReturn(mMockSensorManager).when(mMockContext).getSystemService(Context.SENSOR_SERVICE);
+        mWindowOrientationListener = new TestableWindowOrientationListener(mMockContext,
+                mMockHandler, mMockWindowManagerService);
+        mWindowOrientationListener.mRotationResolverService = mFakeRotationResolverInternal;
+
+        mFakeSensor = new Sensor(mMockInputSensorInfo);
+        mFakeSensorEvent = new SensorEvent(mFakeSensor, /* accuracy */ 1, /* timestamp */ 1L,
+                new float[]{(float) Surface.ROTATION_90});
+    }
+
+    @Test
+    public void testOnSensorChanged_rotationResolverDisabled_useSensorResult() {
+        mRotationResolverEnabled = false;
+
+        mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+        assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90);
+    }
+
+    @Test
+    public void testOnSensorChanged_cannotUseRotationResolver_useSensorResult() {
+        mCanUseRotationResolver = false;
+
+        mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+        assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90);
+
+    }
+
+    @Test
+    public void testOnSensorChanged_normalCase() {
+        mFakeRotationResolverInternal.mResult = Surface.ROTATION_180;
+
+        mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+        assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_180);
+    }
+
+    final class TestableRotationResolver extends RotationResolverInternal {
+        @Surface.Rotation
+        int mResult;
+
+        @Override
+        public boolean isRotationResolverSupported() {
+            return true;
+        }
+
+        @Override
+        public void resolveRotation(@NonNull RotationResolverCallbackInternal callback,
+                @Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation,
+                @DurationMillisLong long timeoutMillis,
+                @NonNull CancellationSignal cancellationSignal) {
+            callback.onSuccess(mResult);
+        }
+    }
+
+    final class TestableWindowOrientationListener extends WindowOrientationListener {
+
+        TestableWindowOrientationListener(Context context, Handler handler,
+                WindowManagerService service) {
+            super(context, handler, service);
+            this.mOrientationJudge = new OrientationSensorJudge();
+        }
+
+        @Override
+        public void onProposedRotationChanged(int rotation) {
+            mFinalizedRotation = rotation;
+        }
+
+        @Override
+        public boolean canUseRotationResolver() {
+            return mCanUseRotationResolver;
+        }
+
+        @Override
+        public boolean isRotationResolverEnabled() {
+            return mRotationResolverEnabled;
+        }
+    }
+}
diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp
index c2cb6881..35ca354 100644
--- a/services/tests/shortcutmanagerutils/Android.bp
+++ b/services/tests/shortcutmanagerutils/Android.bp
@@ -22,5 +22,9 @@
         "android.test.runner.stubs",
     ],
 
+    static_libs: [
+        "compatibility-device-util-axt",
+    ],
+
     sdk_version: "test_current",
 }
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 9f1fff7..5182b3b 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.pm.shortcutmanagertest;
 
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
@@ -34,6 +36,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.Instrumentation;
+import android.app.role.RoleManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.LocusId;
@@ -50,6 +53,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.test.MoreAsserts;
 import android.util.Log;
 
@@ -217,30 +221,60 @@
         return runShortcutCommand(instrumentation, command, result -> result.contains("Success"));
     }
 
-    public static String getDefaultLauncher(Instrumentation instrumentation) {
-        final String PREFIX = "Launcher: ComponentInfo{";
-        final String POSTFIX = "}";
-        final List<String> result = runShortcutCommandForSuccess(
-                instrumentation, "get-default-launcher --user "
-                        + instrumentation.getContext().getUserId());
-        for (String s : result) {
-            if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) {
-                return s.substring(PREFIX.length(), s.length() - POSTFIX.length());
+    private static UserHandle getParentUser(Context context) {
+        final UserHandle user = context.getUser();
+        final UserManager userManager = context.getSystemService(UserManager.class);
+        if (!userManager.isManagedProfile(user.getIdentifier())) {
+            return user;
+        }
+
+        final List<UserHandle> profiles = userManager.getUserProfiles();
+        for (UserHandle handle : profiles) {
+            if (!userManager.isManagedProfile(handle.getIdentifier())) {
+                return handle;
             }
         }
-        fail("Default launcher not found");
         return null;
     }
 
-    public static void setDefaultLauncher(Instrumentation instrumentation, String component) {
-        runCommand(instrumentation, "cmd package set-home-activity --user "
-                + instrumentation.getContext().getUserId() + " " + component,
-                result -> result.contains("Success"));
+    public static String getDefaultLauncher(Instrumentation instrumentation) throws Exception {
+        final Context context = instrumentation.getContext();
+        final RoleManager roleManager = context.getSystemService(RoleManager.class);
+        final UserHandle user = getParentUser(context);
+        List<String> roleHolders = callWithShellPermissionIdentity(
+                () -> roleManager.getRoleHoldersAsUser(RoleManager.ROLE_HOME, user));
+        if (roleHolders.size() == 1) {
+            return roleHolders.get(0);
+        }
+        fail("Failed to get the default launcher for user " + context.getUserId());
+        return null;
+    }
+
+    public static void setDefaultLauncher(Instrumentation instrumentation, String packageName) {
+        runCommandForNoOutput(instrumentation, "cmd role add-role-holder --user "
+                + instrumentation.getContext().getUserId() + " " + RoleManager.ROLE_HOME + " "
+                + packageName + " 0");
+        waitUntil("Failed to get shortcut access",
+                () -> hasShortcutAccess(instrumentation, packageName), 20);
     }
 
     public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
-        setDefaultLauncher(instrumentation, packageContext.getPackageName()
-                + "/android.content.pm.cts.shortcutmanager.packages.Launcher");
+        setDefaultLauncher(instrumentation, packageContext.getPackageName());
+    }
+
+    public static boolean hasShortcutAccess(Instrumentation instrumentation, String packageName) {
+        final List<String> result = runShortcutCommandForSuccess(instrumentation,
+                "has-shortcut-access --user " + instrumentation.getContext().getUserId()
+                        + " " + packageName);
+        for (String s : result) {
+            if (s.startsWith("true")) {
+                return true;
+            } else if (s.startsWith("false")) {
+                return false;
+            }
+        }
+        fail("Failed to check shortcut access");
+        return false;
     }
 
     public static void overrideConfig(Instrumentation instrumentation, String config) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 6b69ee9..002859e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -96,6 +96,8 @@
                 .setTask(mTrampolineActivity.getTask())
                 .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TopActivity"))
                 .build();
+        // becomes invisible when covered by mTopActivity
+        mTrampolineActivity.mVisibleRequested = false;
     }
 
     @After
@@ -230,7 +232,6 @@
 
     @Test
     public void testOnActivityLaunchCancelled_finishedBeforeDrawn() {
-        mTopActivity.mVisibleRequested = true;
         doReturn(true).when(mTopActivity).isReportedDrawn();
 
         // Create an activity with different process that meets process switch.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 9385110..72b8439 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2180,6 +2180,7 @@
 
         activity.setOccludesParent(true);
         activity.setVisible(false);
+        activity.mVisibleRequested = false;
         // Can not specify orientation if app isn't visible even though it occludes parent.
         assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation());
         // Can specify orientation if the current orientation candidate is orientation behind.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 22ee886..3a7954b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1424,6 +1424,9 @@
         final ActivityRecord nonTopVisibleActivity =
                 new ActivityBuilder(mAtm).setTask(task).build();
         new ActivityBuilder(mAtm).setTask(task).build();
+        // The scenario we are testing is when the app isn't visible yet.
+        nonTopVisibleActivity.setVisible(false);
+        nonTopVisibleActivity.mVisibleRequested = false;
         doReturn(false).when(nonTopVisibleActivity).attachedToProcess();
         doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleUnchecked();
         doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(),
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 36adf28..37bc23e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -844,6 +844,9 @@
         final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
                 secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN,
                         ACTIVITY_TYPE_STANDARD, false /* onTop */));
+        // Activity should start invisible since we are bringing it to front.
+        singleTaskActivity.setVisible(false);
+        singleTaskActivity.mVisibleRequested = false;
 
         // Create another activity on top of the secondary display.
         final Task topStack = secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4c2d124..b4fd302 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1379,7 +1379,7 @@
     }
 
     @Test
-    public void testNoFixedRotationWithPip() {
+    public void testFixedRotationWithPip() {
         final DisplayContent displayContent = mDefaultDisplay;
         unblockDisplayRotation(displayContent);
         // Make resume-top really update the activity state.
@@ -1406,15 +1406,20 @@
         assertEquals(homeConfigOrientation, displayConfig.orientation);
 
         clearInvocations(mWm);
-        // Leave PiP to fullscreen. The orientation can be updated from
-        // ActivityRecord#reportDescendantOrientationChangeIfNeeded.
-        pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        // Leave PiP to fullscreen. Simulate the step of PipTaskOrganizer that sets the activity
+        // to fullscreen, so fixed rotation will apply on it.
+        pinnedActivity.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         homeActivity.setState(Task.ActivityState.STOPPED, "test");
 
-        assertFalse(displayContent.hasTopFixedRotationLaunchingApp());
-        verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
-        assertEquals(pinnedConfigOrientation, displayConfig.orientation);
+        assertTrue(displayContent.hasTopFixedRotationLaunchingApp());
+        verify(mWm, never()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
+        assertNotEquals(pinnedConfigOrientation, displayConfig.orientation);
+
+        // Assume the animation of PipTaskOrganizer is done and then commit fullscreen to task.
+        pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        displayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
         assertFalse(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging());
+        assertEquals(pinnedConfigOrientation, displayConfig.orientation);
 
         clearInvocations(mWm);
         // Enter PiP from fullscreen. The orientation can be updated from
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index a3ade51..e22cda6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -295,7 +295,7 @@
         homeActivity.mVisibleRequested = true;
         assertFalse(rootHomeTask.isVisible());
 
-        assertEquals(rootWindowContainer.getOrientation(), rootHomeTask.getOrientation());
+        assertEquals(defaultTaskDisplayArea.getOrientation(), rootHomeTask.getOrientation());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 3492d90..eb6c6ed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -890,6 +890,7 @@
                     mTask.moveToFront("createActivity");
                 }
                 // Make visible by default...
+                activity.mVisibleRequested = true;
                 activity.setVisible(true);
             }
 
diff --git a/telephony/java/android/telephony/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java
index 0059ad6..ae7d209 100644
--- a/telephony/java/android/telephony/SignalThresholdInfo.java
+++ b/telephony/java/android/telephony/SignalThresholdInfo.java
@@ -402,29 +402,27 @@
          * @see #getThresholds() for more details on signal strength thresholds
          */
         public @NonNull Builder setThresholds(@NonNull int[] thresholds) {
-            Objects.requireNonNull(thresholds, "thresholds must not be null");
-            if (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
-                    || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED) {
-                throw new IllegalArgumentException(
-                        "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
-                                + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
-            }
-            mThresholds = thresholds.clone();
-            Arrays.sort(mThresholds);
-            return this;
+            return setThresholds(thresholds, false /*isSystem*/);
         }
 
         /**
-         * Set the signal strength thresholds for the corresponding signal measurement type without
-         * the length limitation.
+         * Set the signal strength thresholds for the corresponding signal measurement type.
          *
          * @param thresholds array of integer as the signal threshold values
+         * @param isSystem true is the caller is system which does not have restrictions on
+         *        the length of thresholds array.
          * @return the builder to facilitate the chaining
          *
          * @hide
          */
-        public @NonNull Builder setThresholdsUnlimited(@NonNull int[] thresholds) {
+        public @NonNull Builder setThresholds(@NonNull int[] thresholds, boolean isSystem) {
             Objects.requireNonNull(thresholds, "thresholds must not be null");
+            if (!isSystem && (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+                    || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED)) {
+                throw new IllegalArgumentException(
+                        "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+                                + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
+            }
             mThresholds = thresholds.clone();
             Arrays.sort(mThresholds);
             return this;
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index f39e30b..814ce18 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
@@ -39,6 +40,8 @@
 
 import com.android.internal.telephony.IIntegerConsumer;
 
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -77,55 +80,13 @@
             "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
 
     /**
-     * Receives RCS Feature availability status updates from the ImsService.
-     *
-     * @see #isAvailable(int)
-     * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
-     * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+     * An application can use {@link #addOnAvailabilityChangedListener} to register a
+     * {@link OnAvailabilityChangedListener}, which will notify the user when the RCS feature
+     * availability status updates from the ImsService.
      * @hide
      */
-    public static class AvailabilityCallback {
-
-        private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
-
-            private final AvailabilityCallback mLocalCallback;
-            private Executor mExecutor;
-
-            CapabilityBinder(AvailabilityCallback c) {
-                mLocalCallback = c;
-            }
-
-            @Override
-            public void onCapabilitiesStatusChanged(int config) {
-                if (mLocalCallback == null) return;
-
-                final long callingIdentity = Binder.clearCallingIdentity();
-                try {
-                    mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged(config));
-                } finally {
-                    restoreCallingIdentity(callingIdentity);
-                }
-            }
-
-            @Override
-            public void onQueryCapabilityConfiguration(int capability, int radioTech,
-                    boolean isEnabled) {
-                // This is not used for public interfaces.
-            }
-
-            @Override
-            public void onChangeCapabilityConfigurationError(int capability, int radioTech,
-                    @ImsFeature.ImsCapabilityError int reason) {
-                // This is not used for public interfaces
-            }
-
-            private void setExecutor(Executor executor) {
-                mExecutor = executor;
-            }
-        }
-
-        private final CapabilityBinder mBinder = new CapabilityBinder(this);
-
+    @SystemApi
+    public interface OnAvailabilityChangedListener {
         /**
          * The availability of the feature's capabilities has changed to either available or
          * unavailable.
@@ -136,22 +97,68 @@
          *
          * @param capabilities The new availability of the capabilities.
          */
-        public void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
+        void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities);
+    }
+
+    /**
+     * Receive the availability status changed from the ImsService and pass the status change to
+     * the associated {@link OnAvailabilityChangedListener}
+     */
+    private static class AvailabilityCallbackAdapter {
+
+        private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+            private final OnAvailabilityChangedListener mOnAvailabilityChangedListener;
+            private final Executor mExecutor;
+
+            CapabilityBinder(OnAvailabilityChangedListener listener, Executor executor) {
+                mExecutor = executor;
+                mOnAvailabilityChangedListener = listener;
+            }
+
+            @Override
+            public void onCapabilitiesStatusChanged(int config) {
+                if (mOnAvailabilityChangedListener == null) return;
+
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() ->
+                            mOnAvailabilityChangedListener.onAvailabilityChanged(config));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            @Override
+            public void onQueryCapabilityConfiguration(int capability, int radioTech,
+                    boolean isEnabled) {
+                // This is not used.
+            }
+
+            @Override
+            public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                    @ImsFeature.ImsCapabilityError int reason) {
+                // This is not used.
+            }
+        }
+
+        private final CapabilityBinder mBinder;
+
+        AvailabilityCallbackAdapter(@NonNull Executor executor,
+                @NonNull OnAvailabilityChangedListener listener) {
+            mBinder = new CapabilityBinder(listener, executor);
         }
 
         /**@hide*/
         public final IImsCapabilityCallback getBinder() {
             return mBinder;
         }
-
-        private void setExecutor(Executor executor) {
-            mBinder.setExecutor(executor);
-        }
     }
 
     private final int mSubId;
     private final Context mContext;
     private final BinderCacheManager<IImsRcsController> mBinderCache;
+    private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter>
+            mAvailabilityChangedCallbacks;
 
     /**
      * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class.
@@ -162,6 +169,7 @@
         mSubId = subId;
         mContext = context;
         mBinderCache = binderCache;
+        mAvailabilityChangedCallbacks = new HashMap<>();
     }
 
     /**
@@ -174,10 +182,23 @@
     }
 
     /**
-     * @hide
+     * Registers a {@link RegistrationManager.RegistrationCallback} with the system. When the
+     * callback is registered, it will initiate the callback c to be called with the current
+     * registration state.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param executor The executor the callback events should be run on.
+     * @param c The {@link RegistrationManager.RegistrationCallback} to be added.
+     * @see #unregisterImsRegistrationCallback(RegistrationManager.RegistrationCallback)
+     * @throws ImsException if the subscription associated with this callback is valid, but
+     * the {@link ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+     * reason.
      */
-    // @Override add back to RegistrationManager interface once public.
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void registerImsRegistrationCallback(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull RegistrationManager.RegistrationCallback c)
@@ -191,7 +212,7 @@
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "Register registration callback: IImsRcsController is null");
+            Log.w(TAG, "Register registration callback: IImsRcsController is null");
             throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
@@ -207,10 +228,21 @@
     }
 
     /**
-     * @hide
+     * Removes an existing {@link RegistrationManager.RegistrationCallback}.
+     *
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be removed. If this method is called for an
+     * inactive subscription, it will result in a no-op.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param c The {@link RegistrationManager.RegistrationCallback} to be removed.
+     * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener
+     * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
      */
-    // @Override add back to RegistrationManager interface once public.
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void unregisterImsRegistrationCallback(
             @NonNull RegistrationManager.RegistrationCallback c) {
         if (c == null) {
@@ -219,7 +251,7 @@
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "Unregister registration callback: IImsRcsController is null");
+            Log.w(TAG, "Unregister registration callback: IImsRcsController is null");
             throw new IllegalStateException("Cannot find remote IMS service");
         }
 
@@ -231,10 +263,21 @@
     }
 
     /**
-     * @hide
+     * Gets the registration state of the IMS service.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param executor The {@link Executor} that will be used to call the IMS registration state
+     * callback.
+     * @param stateCallback A callback called on the supplied {@link Executor} that will contain the
+     * registration state of the IMS service, which will be one of the
+     * following: {@link RegistrationManager#REGISTRATION_STATE_NOT_REGISTERED},
+     * {@link RegistrationManager#REGISTRATION_STATE_REGISTERING}, or
+     * {@link RegistrationManager#REGISTRATION_STATE_REGISTERED}.
      */
-    // @Override add back to RegistrationManager interface once public.
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
             @NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback) {
         if (stateCallback == null) {
@@ -246,7 +289,7 @@
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "Get registration state error: IImsRcsController is null");
+            Log.w(TAG, "Get registration state error: IImsRcsController is null");
             throw new IllegalStateException("Cannot find remote IMS service");
         }
 
@@ -263,9 +306,20 @@
     }
 
     /**
-     * @hide
+     * Gets the Transport Type associated with the current IMS registration.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param executor The {@link Executor} that will be used to call the transportTypeCallback.
+     * @param transportTypeCallback The transport type associated with the current IMS registration,
+     * which will be one of following:
+     * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN},
+     * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or
+     * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}.
      */
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
             @NonNull @AccessNetworkConstants.TransportType
                     Consumer<Integer> transportTypeCallback) {
@@ -278,7 +332,7 @@
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "Get registration transport type error: IImsRcsController is null");
+            Log.w(TAG, "Get registration transport type error: IImsRcsController is null");
             throw new IllegalStateException("Cannot find remote IMS service");
         }
 
@@ -296,31 +350,33 @@
     }
 
     /**
-     * Registers an {@link AvailabilityCallback} with the system, which will provide RCS
+     * Add an {@link OnAvailabilityChangedListener} with the system, which will provide RCS
      * availability updates for the subscription specified.
      *
      * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
      * subscription changed events and call
-     * {@link #unregisterRcsAvailabilityCallback(AvailabilityCallback)} to clean up after a
-     * subscription is removed.
+     * {@link #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)} to clean up
+     * after a subscription is removed.
      * <p>
-     * When the callback is registered, it will initiate the callback c to be called with the
-     * current capabilities.
+     * When the listener is registered, it will initiate the callback listener to be called with
+     * the current capabilities.
      *
      * @param executor The executor the callback events should be run on.
-     * @param c The RCS {@link AvailabilityCallback} to be registered.
-     * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+     * @param listener The RCS {@link OnAvailabilityChangedListener} to be registered.
+     * @see #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)
      * @throws ImsException if the subscription associated with this instance of
      * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public void registerRcsAvailabilityCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull AvailabilityCallback c) throws ImsException {
-        if (c == null) {
-            throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+    public void addOnAvailabilityChangedListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OnAvailabilityChangedListener listener) throws ImsException {
+        if (listener == null) {
+            throw new IllegalArgumentException("Must include a non-null"
+                    + "OnAvailabilityChangedListener.");
         }
         if (executor == null) {
             throw new IllegalArgumentException("Must include a non-null Executor.");
@@ -328,56 +384,61 @@
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "Register availability callback: IImsRcsController is null");
+            Log.w(TAG, "Add availability changed listener: IImsRcsController is null");
             throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
 
-        c.setExecutor(executor);
+        AvailabilityCallbackAdapter adapter =
+                addAvailabilityChangedListenerToCollection(executor, listener);
         try {
-            imsRcsController.registerRcsAvailabilityCallback(mSubId, c.getBinder());
-
+            imsRcsController.registerRcsAvailabilityCallback(mSubId, adapter.getBinder());
         } catch (ServiceSpecificException e) {
             throw new ImsException(e.toString(), e.errorCode);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e);
+            Log.w(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e);
             throw new ImsException("Remote IMS Service is not available",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
-    /**
-     * Removes an existing RCS {@link AvailabilityCallback}.
+     /**
+     * Removes an existing RCS {@link OnAvailabilityChangedListener}.
      * <p>
      * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
      * etc...), this callback will automatically be unregistered. If this method is called for an
      * inactive subscription, it will result in a no-op.
-     * @param c The RCS {@link AvailabilityCallback} to be removed.
-     * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
+     * @param listener The RCS {@link OnAvailabilityChangedListener} to be removed.
+     * @see #addOnAvailabilityChangedListener(Executor, OnAvailabilityChangedListener)
      * @throws ImsException if the IMS service is not available when calling this method.
      * See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c)
-            throws ImsException {
-        if (c == null) {
-            throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+    public void removeOnAvailabilityChangedListener(
+            @NonNull OnAvailabilityChangedListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("Must include a non-null"
+                    + "OnAvailabilityChangedListener.");
         }
 
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "Unregister availability callback: IImsRcsController is null");
-            throw new ImsException("Cannot find remote IMS service",
-                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+            Log.w(TAG, "Remove availability changed listener: IImsRcsController is null");
+            return;
+        }
+
+        AvailabilityCallbackAdapter callback =
+                removeAvailabilityChangedListenerFromCollection(listener);
+        if (callback == null) {
+            return;
         }
 
         try {
-            imsRcsController.unregisterRcsAvailabilityCallback(mSubId, c.getBinder());
+            imsRcsController.unregisterRcsAvailabilityCallback(mSubId, callback.getBinder());
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e);
-            throw new ImsException("Remote IMS Service is not available",
-                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+            Log.w(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e);
         }
     }
 
@@ -388,26 +449,24 @@
      * RCS capabilities provided over-the-top by applications.
      *
      * @param capability The RCS capability to query.
-     * @param radioTech The radio tech that this capability failed for, defined as
-     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
-     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} or
-     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
+     * @param radioTech The radio technology type that we are querying.
      * @return true if the RCS capability is capable for this subscription, false otherwise. This
      * does not necessarily mean that we are registered for IMS and the capability is available, but
      * rather the subscription is capable of this service over IMS.
-     * @see #isAvailable(int)
+     * @see #isAvailable(int, int)
      * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
      * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL
      * @throws ImsException if the IMS service is not available when calling this method.
      * See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "isCapable: IImsRcsController is null");
+            Log.w(TAG, "isCapable: IImsRcsController is null");
             throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
@@ -415,7 +474,7 @@
         try {
             return imsRcsController.isCapable(mSubId, capability, radioTech);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling IImsRcsController#isCapable", e);
+            Log.w(TAG, "Error calling IImsRcsController#isCapable", e);
             throw new ImsException("Remote IMS Service is not available",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
@@ -428,6 +487,7 @@
      * RCS capabilities provided by over-the-top by applications.
      *
      * @param capability the RCS capability to query.
+     * @param radioTech The radio technology type that we are querying.
      * @return true if the RCS capability is currently available for the associated subscription,
      * false otherwise. If the capability is available, IMS is registered and the service is
      * currently available over IMS.
@@ -436,25 +496,57 @@
      * See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability)
+    public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)
             throws ImsException {
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
-            Log.e(TAG, "isAvailable: IImsRcsController is null");
+            Log.w(TAG, "isAvailable: IImsRcsController is null");
             throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
 
         try {
-            return imsRcsController.isAvailable(mSubId, capability);
+            return imsRcsController.isAvailable(mSubId, capability, radioTech);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling IImsRcsController#isAvailable", e);
+            Log.w(TAG, "Error calling IImsRcsController#isAvailable", e);
             throw new ImsException("Remote IMS Service is not available",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
     }
 
+    /**
+     * Add the {@link OnAvailabilityChangedListener} to collection for tracking.
+     * @param executor The executor that will be used when the publish state is changed and the
+     * {@link OnAvailabilityChangedListener} is called.
+     * @param listener The {@link OnAvailabilityChangedListener} to call the publish state changed.
+     * @return The {@link AvailabilityCallbackAdapter} to wrapper the
+     * {@link OnAvailabilityChangedListener}
+     */
+    private AvailabilityCallbackAdapter addAvailabilityChangedListenerToCollection(
+            @NonNull Executor executor, @NonNull OnAvailabilityChangedListener listener) {
+        AvailabilityCallbackAdapter adapter = new AvailabilityCallbackAdapter(executor, listener);
+        synchronized (mAvailabilityChangedCallbacks) {
+            mAvailabilityChangedCallbacks.put(listener, adapter);
+        }
+        return adapter;
+    }
+
+    /**
+     * Remove the existing {@link OnAvailabilityChangedListener} from the collection.
+     * @param listener The {@link OnAvailabilityChangedListener} to remove from the collection.
+     * @return The wrapper class {@link AvailabilityCallbackAdapter} associated with the
+     * {@link OnAvailabilityChangedListener}.
+     */
+    private AvailabilityCallbackAdapter removeAvailabilityChangedListenerFromCollection(
+            @NonNull OnAvailabilityChangedListener listener) {
+        synchronized (mAvailabilityChangedCallbacks) {
+            return mAvailabilityChangedCallbacks.remove(listener);
+        }
+    }
+
     private IImsRcsController getIImsRcsController() {
         IBinder binder = TelephonyFrameworkInitializer
                 .getTelephonyServiceManager()
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 7a6c28b..8931a78 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -47,7 +47,7 @@
     void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
     void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
     boolean isCapable(int subId, int capability, int radioTech);
-    boolean isAvailable(int subId, int capability);
+    boolean isAvailable(int subId, int capability, int radioTech);
 
     // ImsUceAdapter specific
     void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
diff --git a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
index c5b1c90..f3791d1 100644
--- a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -36,12 +36,9 @@
 public final class CapabilityChangeRequest implements Parcelable {
 
     /**
-     * Contains a feature capability, defined as
-     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
-     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
-     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
-     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS},
-     * along with an associated technology, defined as
+     * Contains a MMTEL feature capability {@link MmTelFeature.MmTelCapabilities} and RCS feature
+     * capability {@link RcsFeature.RcsImsCapabilities}, along with an associated technology,
+     * defined as
      * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
      * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
      * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
@@ -50,7 +47,7 @@
         private final int mCapability;
         private final int radioTech;
 
-        public CapabilityPair(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+        public CapabilityPair(int capability,
                 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
             this.mCapability = capability;
             this.radioTech = radioTech;
@@ -81,13 +78,10 @@
         }
 
         /**
-         * @return The stored capability, defined as
-         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
-         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
-         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
-         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+         * @return The stored capability, defined as {@link MmTelFeature.MmTelCapabilities} and
+         * {@link RcsFeature.RcsImsCapabilities}
          */
-        public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() {
+        public int getCapability() {
             return mCapability;
         }
 
@@ -125,12 +119,11 @@
      * Add one or many capabilities to the request to be enabled.
      *
      * @param capabilities A bitfield of capabilities to enable, valid values are defined in
-     *   {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+     *   {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}.
      * @param radioTech  the radio tech that these capabilities should be enabled for, valid
      *   values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
      */
-    public void addCapabilitiesToEnableForTech(
-            @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+    public void addCapabilitiesToEnableForTech(int capabilities,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech);
     }
@@ -138,12 +131,11 @@
     /**
      * Add one or many capabilities to the request to be disabled.
      * @param capabilities A bitfield of capabilities to diable, valid values are defined in
-     *   {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+     *   {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}.
      * @param radioTech the radio tech that these capabilities should be disabled for, valid
      *   values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
      */
-    public void addCapabilitiesToDisableForTech(
-            @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+    public void addCapabilitiesToDisableForTech(int capabilities,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech);
     }
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 22df921..85703f8 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -194,7 +194,6 @@
      * of the capability and notify the capability status as true using
      * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
      * framework that the capability is available for usage.
-     * @hide
      */
     public static class RcsImsCapabilities extends Capabilities {
         /** @hide*/
@@ -226,12 +225,21 @@
          */
         public static final int CAPABILITY_TYPE_PRESENCE_UCE =  1 << 1;
 
+        /**
+         * Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
+         * @param capabilities The capabilities that are supported for RCS in the form of a
+         * bitfield.
+         */
         public RcsImsCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
             super(capabilities);
         }
 
-        private RcsImsCapabilities(Capabilities c) {
-            super(c.getMask());
+        /**
+         * Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
+         * @param capabilities The capabilities instance that are supported for RCS
+         */
+        private RcsImsCapabilities(Capabilities capabilities) {
+            super(capabilities.getMask());
         }
 
         @Override
@@ -307,7 +315,7 @@
      * set, the {@link RcsFeature} has brought up the capability and is ready for framework
      * requests. To change the status of the capabilities
      * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
-     * @hide
+     * @return A copy of the current RcsFeature capability status.
      */
     @Override
     public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
@@ -318,13 +326,13 @@
      * Notify the framework that the capabilities status has changed. If a capability is enabled,
      * this signals to the framework that the capability has been initialized and is ready.
      * Call {@link #queryCapabilityStatus()} to return the current capability status.
-     * @hide
+     * @param capabilities The current capability status of the RcsFeature.
      */
-    public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
-        if (c == null) {
+    public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) {
+        if (capabilities == null) {
             throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
         }
-        super.notifyCapabilitiesStatusChanged(c);
+        super.notifyCapabilitiesStatusChanged(capabilities);
     }
 
     /**
@@ -333,7 +341,9 @@
      * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to
      * enable or disable capability A, this method should return the correct configuration for
      * capability A afterwards (until it has changed).
-     * @hide
+     * @param capability The capability that we are querying the configuration for.
+     * @param radioTech The radio technology type that we are querying.
+     * @return true if the capability is enabled, false otherwise.
      */
     public boolean queryCapabilityConfiguration(
             @RcsUceAdapter.RcsImsCapabilityFlag int capability,
@@ -355,11 +365,12 @@
      * If for some reason one or more of these capabilities can not be enabled/disabled,
      * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should
      * be called for each capability change that resulted in an error.
-     * @hide
+     * @param request The request to change the capability.
+     * @param callback To notify the framework that the result of the capability changes.
      */
     @Override
     public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
-            @NonNull CapabilityCallbackProxy c) {
+            @NonNull CapabilityCallbackProxy callback) {
         // Base Implementation - Override to provide functionality
     }
 
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index e68fbd8..b2719fb 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -25,7 +25,6 @@
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.server.wm.flicker"/>
-        <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
         <option name="shell-timeout" value="6600s" />
         <option name="test-timeout" value="6600s" />
         <option name="hidden-api-checks" value="false" />
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 89c6663..c5447c1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -17,8 +17,11 @@
 package com.android.server.wm.flicker
 
 import android.platform.helpers.IAppHelper
+import com.android.server.wm.flicker.dsl.EventLogAssertionBuilder
 import com.android.server.wm.flicker.dsl.EventLogAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
 import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.WmAssertionBuilder
 import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME
@@ -31,115 +34,88 @@
 const val WALLPAPER_TITLE = "Wallpaper"
 
 @JvmOverloads
-fun WmAssertionBuilderLegacy.statusBarWindowIsAlwaysVisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("statusBarWindowIsAlwaysVisible", bugId, enabled) {
-        this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME)
+fun WmAssertionBuilder.statusBarWindowIsAlwaysVisible(bugId: Int = 0) {
+    all("statusBarWindowIsAlwaysVisible", bugId) {
+        this.showsAboveAppWindow(NAV_BAR_LAYER_NAME)
     }
 }
 
 @JvmOverloads
-fun WmAssertionBuilderLegacy.navBarWindowIsAlwaysVisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("navBarWindowIsAlwaysVisible", bugId, enabled) {
-        this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME)
+fun WmAssertionBuilder.navBarWindowIsAlwaysVisible(bugId: Int = 0) {
+    all("navBarWindowIsAlwaysVisible", bugId) {
+        this.showsAboveAppWindow(NAV_BAR_LAYER_NAME)
     }
 }
 
-fun WmAssertionBuilderLegacy.visibleWindowsShownMoreThanOneConsecutiveEntry(
+fun WmAssertionBuilder.visibleWindowsShownMoreThanOneConsecutiveEntry(
     ignoreWindows: List<String> = emptyList(),
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
+    bugId: Int = 0
 ) {
-    all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId, enabled) {
+    all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId) {
         this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows)
     }
 }
 
-fun WmAssertionBuilderLegacy.launcherReplacesAppWindowAsTopWindow(
-    testApp: IAppHelper,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("launcherReplacesAppWindowAsTopWindow", bugId, enabled) {
+fun WmAssertionBuilder.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper, bugId: Int = 0) {
+    all("launcherReplacesAppWindowAsTopWindow", bugId) {
         this.showsAppWindowOnTop(testApp.getPackage())
                 .then()
                 .showsAppWindowOnTop("Launcher")
     }
 }
 
-fun WmAssertionBuilderLegacy.wallpaperWindowBecomesVisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("wallpaperWindowBecomesVisible", bugId, enabled) {
+fun WmAssertionBuilder.wallpaperWindowBecomesVisible(bugId: Int = 0) {
+    all("wallpaperWindowBecomesVisible", bugId) {
         this.hidesBelowAppWindow(WALLPAPER_TITLE)
                 .then()
                 .showsBelowAppWindow(WALLPAPER_TITLE)
     }
 }
 
-fun WmAssertionBuilderLegacy.wallpaperWindowBecomesInvisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("wallpaperWindowBecomesInvisible", bugId, enabled) {
+fun WmAssertionBuilder.wallpaperWindowBecomesInvisible(bugId: Int = 0) {
+    all("wallpaperWindowBecomesInvisible", bugId) {
         this.showsBelowAppWindow("Wallpaper")
                 .then()
                 .hidesBelowAppWindow("Wallpaper")
     }
 }
 
-fun WmAssertionBuilderLegacy.appWindowAlwaysVisibleOnTop(
+fun WmAssertionBuilder.appWindowAlwaysVisibleOnTop(
     packageName: String,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
+    bugId: Int = 0
 ) {
-    all("appWindowAlwaysVisibleOnTop", bugId, enabled) {
+    all("appWindowAlwaysVisibleOnTop", bugId) {
         this.showsAppWindowOnTop(packageName)
     }
 }
 
-fun WmAssertionBuilderLegacy.appWindowBecomesVisible(
-    appName: String,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("appWindowBecomesVisible", bugId, enabled) {
+fun WmAssertionBuilder.appWindowBecomesVisible(appName: String, bugId: Int = 0) {
+    all("appWindowBecomesVisible", bugId) {
         this.hidesAppWindow(appName)
                 .then()
                 .showsAppWindow(appName)
     }
 }
 
-fun WmAssertionBuilderLegacy.appWindowBecomesInVisible(
-    appName: String,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("appWindowBecomesInVisible", bugId, enabled) {
+fun WmAssertionBuilder.appWindowBecomesInVisible(appName: String, bugId: Int = 0) {
+    all("appWindowBecomesInVisible", bugId) {
         this.showsAppWindow(appName)
-                .then()
-                .hidesAppWindow(appName)
+            .then()
+            .hidesAppWindow(appName)
     }
 }
 
 @JvmOverloads
-fun LayersAssertionBuilderLegacy.noUncoveredRegions(
+fun LayersAssertionBuilder.noUncoveredRegions(
     beginRotation: Int,
     endRotation: Int = beginRotation,
     allStates: Boolean = true,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
+    bugId: Int = 0
 ) {
     val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
     val endingBounds = WindowUtils.getDisplayBounds(endRotation)
     if (allStates) {
-        all("noUncoveredRegions", bugId, enabled) {
+        all("noUncoveredRegions", bugId) {
             if (startingBounds == endingBounds) {
                 this.coversAtLeastRegion(startingBounds)
             } else {
@@ -159,6 +135,284 @@
 }
 
 @JvmOverloads
+fun LayersAssertionBuilder.navBarLayerIsAlwaysVisible(
+    rotatesScreen: Boolean = false,
+    bugId: Int = 0
+) {
+    if (rotatesScreen) {
+        all("navBarLayerIsAlwaysVisible", bugId) {
+            this.showsLayer(NAV_BAR_LAYER_NAME)
+                    .then()
+                    .hidesLayer(NAV_BAR_LAYER_NAME)
+                    .then()
+                    .showsLayer(NAV_BAR_LAYER_NAME)
+        }
+    } else {
+        all("navBarLayerIsAlwaysVisible", bugId) {
+            this.showsLayer(NAV_BAR_LAYER_NAME)
+        }
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.statusBarLayerIsAlwaysVisible(
+    rotatesScreen: Boolean = false,
+    bugId: Int = 0
+) {
+    if (rotatesScreen) {
+        all("statusBarLayerIsAlwaysVisible", bugId) {
+            this.showsLayer(STATUS_BAR_WINDOW_NAME)
+                    .then()
+                    hidesLayer(STATUS_BAR_WINDOW_NAME)
+                    .then()
+                    .showsLayer(STATUS_BAR_WINDOW_NAME)
+        }
+    } else {
+        all("statusBarLayerIsAlwaysVisible", bugId) {
+            this.showsLayer(STATUS_BAR_WINDOW_NAME)
+        }
+    }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.navBarLayerRotatesAndScales(
+    beginRotation: Int,
+    endRotation: Int = beginRotation,
+    bugId: Int = 0
+) {
+    val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
+    val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
+
+    start("navBarLayerRotatesAndScales_StartingPos", bugId) {
+        this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos)
+    }
+    end("navBarLayerRotatesAndScales_EndingPost", bugId) {
+        this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos)
+    }
+
+    /*if (startingPos == endingPos) {
+        all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) {
+            this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+        }
+    }*/
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.statusBarLayerRotatesScales(
+    beginRotation: Int,
+    endRotation: Int = beginRotation,
+    bugId: Int = 0
+) {
+    val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
+    val endingPos = WindowUtils.getStatusBarPosition(endRotation)
+
+    start("statusBarLayerRotatesScales_StartingPos", bugId) {
+        this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, startingPos)
+    }
+    end("statusBarLayerRotatesScales_EndingPos", bugId) {
+        this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, endingPos)
+    }
+}
+
+fun LayersAssertionBuilder.visibleLayersShownMoreThanOneConsecutiveEntry(
+    ignoreLayers: List<String> = emptyList(),
+    bugId: Int = 0
+) {
+    all("visibleLayersShownMoreThanOneConsecutiveEntry", bugId) {
+        this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoreLayers)
+    }
+}
+
+fun LayersAssertionBuilder.appLayerReplacesWallpaperLayer(appName: String, bugId: Int = 0) {
+    all("appLayerReplacesWallpaperLayer", bugId) {
+        this.showsLayer("Wallpaper")
+                .then()
+                .replaceVisibleLayer("Wallpaper", appName)
+    }
+}
+
+fun LayersAssertionBuilder.wallpaperLayerReplacesAppLayer(testApp: IAppHelper, bugId: Int = 0) {
+    all("appLayerReplacesWallpaperLayer", bugId) {
+        this.showsLayer(testApp.getPackage())
+                .then()
+                .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
+    }
+}
+
+fun LayersAssertionBuilder.layerAlwaysVisible(packageName: String, bugId: Int = 0) {
+    all("layerAlwaysVisible", bugId) {
+        this.showsLayer(packageName)
+    }
+}
+
+fun LayersAssertionBuilder.layerBecomesVisible(packageName: String, bugId: Int = 0) {
+    all("layerBecomesVisible", bugId) {
+        this.hidesLayer(packageName)
+                .then()
+                .showsLayer(packageName)
+    }
+}
+
+fun LayersAssertionBuilder.layerBecomesInvisible(packageName: String, bugId: Int = 0) {
+    all("layerBecomesInvisible", bugId) {
+        this.showsLayer(packageName)
+                .then()
+                .hidesLayer(packageName)
+    }
+}
+
+fun EventLogAssertionBuilder.focusChanges(vararg windows: String, bugId: Int = 0) {
+    all("focusChanges", bugId) {
+        this.focusChanges(windows)
+    }
+}
+
+fun EventLogAssertionBuilder.focusDoesNotChange(bugId: Int = 0) {
+    all("focusDoesNotChange", bugId) {
+        this.focusDoesNotChange()
+    }
+}
+
+@JvmOverloads
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.statusBarWindowIsAlwaysVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("statusBarWindowIsAlwaysVisible", bugId, enabled) {
+        this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME)
+    }
+}
+
+@JvmOverloads
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.navBarWindowIsAlwaysVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("navBarWindowIsAlwaysVisible", bugId, enabled) {
+        this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME)
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.visibleWindowsShownMoreThanOneConsecutiveEntry(
+    ignoreWindows: List<String> = emptyList(),
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId, enabled) {
+        this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows)
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.launcherReplacesAppWindowAsTopWindow(
+    testApp: IAppHelper,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("launcherReplacesAppWindowAsTopWindow", bugId, enabled) {
+        this.showsAppWindowOnTop(testApp.getPackage())
+            .then()
+            .showsAppWindowOnTop("Launcher")
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.wallpaperWindowBecomesVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("wallpaperWindowBecomesVisible", bugId, enabled) {
+        this.hidesBelowAppWindow(WALLPAPER_TITLE)
+            .then()
+            .showsBelowAppWindow(WALLPAPER_TITLE)
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.wallpaperWindowBecomesInvisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("wallpaperWindowBecomesInvisible", bugId, enabled) {
+        this.showsBelowAppWindow("Wallpaper")
+            .then()
+            .hidesBelowAppWindow("Wallpaper")
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.appWindowAlwaysVisibleOnTop(
+    packageName: String,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("appWindowAlwaysVisibleOnTop", bugId, enabled) {
+        this.showsAppWindowOnTop(packageName)
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.appWindowBecomesVisible(
+    appName: String,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("appWindowBecomesVisible", bugId, enabled) {
+        this.hidesAppWindow(appName)
+            .then()
+            .showsAppWindow(appName)
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.appWindowBecomesInVisible(
+    appName: String,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("appWindowBecomesInVisible", bugId, enabled) {
+        this.showsAppWindow(appName)
+            .then()
+            .hidesAppWindow(appName)
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+@JvmOverloads
+fun LayersAssertionBuilderLegacy.noUncoveredRegions(
+    beginRotation: Int,
+    endRotation: Int = beginRotation,
+    allStates: Boolean = true,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
+    val endingBounds = WindowUtils.getDisplayBounds(endRotation)
+    if (allStates) {
+        all("noUncoveredRegions", bugId, enabled) {
+            if (startingBounds == endingBounds) {
+                this.coversAtLeastRegion(startingBounds)
+            } else {
+                this.coversAtLeastRegion(startingBounds)
+                    .then()
+                    .coversAtLeastRegion(endingBounds)
+            }
+        }
+    } else {
+        start("noUncoveredRegions_StartingPos") {
+            this.coversAtLeastRegion(startingBounds)
+        }
+        end("noUncoveredRegions_EndingPos") {
+            this.coversAtLeastRegion(endingBounds)
+        }
+    }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+@JvmOverloads
 fun LayersAssertionBuilderLegacy.navBarLayerIsAlwaysVisible(
     rotatesScreen: Boolean = false,
     bugId: Int = 0,
@@ -167,10 +421,10 @@
     if (rotatesScreen) {
         all("navBarLayerIsAlwaysVisible", bugId, enabled) {
             this.showsLayer(NAV_BAR_LAYER_NAME)
-                    .then()
-                    .hidesLayer(NAV_BAR_LAYER_NAME)
-                    .then()
-                    .showsLayer(NAV_BAR_LAYER_NAME)
+                .then()
+                .hidesLayer(NAV_BAR_LAYER_NAME)
+                .then()
+                .showsLayer(NAV_BAR_LAYER_NAME)
         }
     } else {
         all("navBarLayerIsAlwaysVisible", bugId, enabled) {
@@ -179,6 +433,7 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 @JvmOverloads
 fun LayersAssertionBuilderLegacy.statusBarLayerIsAlwaysVisible(
     rotatesScreen: Boolean = false,
@@ -188,10 +443,10 @@
     if (rotatesScreen) {
         all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
             this.showsLayer(STATUS_BAR_LAYER_NAME)
-                    .then()
-                    hidesLayer(STATUS_BAR_LAYER_NAME)
-                    .then()
-                    .showsLayer(STATUS_BAR_LAYER_NAME)
+                .then()
+                .hidesLayer(STATUS_BAR_LAYER_NAME)
+                .then()
+                .showsLayer(STATUS_BAR_LAYER_NAME)
         }
     } else {
         all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
@@ -200,6 +455,7 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 @JvmOverloads
 fun LayersAssertionBuilderLegacy.navBarLayerRotatesAndScales(
     beginRotation: Int,
@@ -224,6 +480,7 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 @JvmOverloads
 fun LayersAssertionBuilderLegacy.statusBarLayerRotatesScales(
     beginRotation: Int,
@@ -242,8 +499,9 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun LayersAssertionBuilderLegacy.visibleLayersShownMoreThanOneConsecutiveEntry(
-    ignoreLayers: List<String> = emptyList(),
+    ignoreLayers: List<String> = kotlin.collections.emptyList(),
     bugId: Int = 0,
     enabled: Boolean = bugId == 0
 ) {
@@ -252,6 +510,7 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun LayersAssertionBuilderLegacy.appLayerReplacesWallpaperLayer(
     appName: String,
     bugId: Int = 0,
@@ -259,11 +518,12 @@
 ) {
     all("appLayerReplacesWallpaperLayer", bugId, enabled) {
         this.showsLayer("Wallpaper")
-                .then()
-                .replaceVisibleLayer("Wallpaper", appName)
+            .then()
+            .replaceVisibleLayer("Wallpaper", appName)
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun LayersAssertionBuilderLegacy.wallpaperLayerReplacesAppLayer(
     testApp: IAppHelper,
     bugId: Int = 0,
@@ -271,11 +531,12 @@
 ) {
     all("appLayerReplacesWallpaperLayer", bugId, enabled) {
         this.showsLayer(testApp.getPackage())
-                .then()
-                .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
+            .then()
+            .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun LayersAssertionBuilderLegacy.layerAlwaysVisible(
     packageName: String,
     bugId: Int = 0,
@@ -286,6 +547,7 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun LayersAssertionBuilderLegacy.layerBecomesVisible(
     packageName: String,
     bugId: Int = 0,
@@ -293,11 +555,12 @@
 ) {
     all("layerBecomesVisible", bugId, enabled) {
         this.hidesLayer(packageName)
-                .then()
-                .showsLayer(packageName)
+            .then()
+            .showsLayer(packageName)
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun LayersAssertionBuilderLegacy.layerBecomesInvisible(
     packageName: String,
     bugId: Int = 0,
@@ -305,11 +568,12 @@
 ) {
     all("layerBecomesInvisible", bugId, enabled) {
         this.showsLayer(packageName)
-                .then()
-                .hidesLayer(packageName)
+            .then()
+            .hidesLayer(packageName)
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun EventLogAssertionBuilderLegacy.focusChanges(
     vararg windows: String,
     bugId: Int = 0,
@@ -320,6 +584,7 @@
     }
 }
 
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
 fun EventLogAssertionBuilderLegacy.focusDoesNotChange(
     bugId: Int = 0,
     enabled: Boolean = bugId == 0
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index b5fd4a5..c507841 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.close
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -50,7 +49,6 @@
  * Test app closes by pressing back button
  * To run this test: `atest FlickerTests:CloseAppBackButtonTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -65,7 +63,7 @@
             val testApp = SimpleAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
-                    withTestName { buildTestTag("closeAppBackButton", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -89,29 +87,47 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            launcherReplacesAppWindowAsTopWindow(testApp)
-                            wallpaperWindowBecomesVisible()
+                        presubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                launcherReplacesAppWindowAsTopWindow(testApp)
+                                wallpaperWindowBecomesVisible()
+                            }
+
+                            layersTrace {
+                                noUncoveredRegions(configuration.startRotation,
+                                    Surface.ROTATION_0)
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                wallpaperLayerReplacesAppLayer(testApp)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                }
+                            }
                         }
 
-                        layersTrace {
-                            noUncoveredRegions(configuration.startRotation,
-                                Surface.ROTATION_0)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                Surface.ROTATION_0,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(configuration.startRotation,
-                                Surface.ROTATION_0,
-                                enabled = !configuration.startRotation.isRotated())
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+                        flaky {
+                            windowManagerTrace {
+                                visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+                            }
 
-                            wallpaperLayerReplacesAppLayer(testApp)
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 584e4b1..d1c3efe 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -37,6 +37,7 @@
 import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.isRotated
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
@@ -64,7 +65,7 @@
             val testApp = SimpleAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
-                    withTestName { buildTestTag("closeAppHomeButton", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -88,28 +89,46 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173689015)
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            launcherReplacesAppWindowAsTopWindow(testApp)
-                            wallpaperWindowBecomesVisible()
+                        presubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                launcherReplacesAppWindowAsTopWindow(testApp)
+                                wallpaperWindowBecomesVisible()
+                            }
+
+                            layersTrace {
+                                noUncoveredRegions(configuration.startRotation,
+                                    Surface.ROTATION_0)
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                wallpaperLayerReplacesAppLayer(testApp)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                }
+                            }
                         }
 
-                        layersTrace {
-                            val isRotation0 = configuration.startRotation == Surface.ROTATION_0
-                            noUncoveredRegions(configuration.startRotation,
-                                Surface.ROTATION_0)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                Surface.ROTATION_0, enabled = isRotation0)
-                            statusBarLayerRotatesScales(configuration.startRotation,
-                                Surface.ROTATION_0, enabled = isRotation0)
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173689015)
+                        flaky {
+                            windowManagerTrace {
+                                visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173689015)
+                            }
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173689015)
 
-                            wallpaperLayerReplacesAppLayer(testApp)
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
index 1a47449..323236e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -43,7 +43,7 @@
         wmHelper.waitForAppTransitionIdle()
 
         // Ensure WindowManagerService wait until all animations have completed
-        instrumentation.getUiAutomation().syncInputTransactions()
+        instrumentation.uiAutomation.syncInputTransactions()
     } catch (e: RemoteException) {
         throw RuntimeException(e)
     }
@@ -71,8 +71,8 @@
 /**
  * Build a test tag for the test
  * @param testName Name of the transition(s) being tested
- * @param app App being launcher
  * @param configuration Configuration for the test
+ * @param extraInfo Additional information to append to the tag
  *
  * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
 </END_ROTATION></BEGIN_ROTATION></APP></NAME> */
@@ -92,9 +92,30 @@
 
 /**
  * Build a test tag for the test
+ * @param configuration Configuration for the test
+ * @param extraInfo Additional information to append to the tag
+ *
+ * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
+</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
+@JvmOverloads
+fun buildTestTag(
+    configuration: Bundle,
+    extraInfo: String = ""
+): String {
+    return buildTestTag(testName = null,
+        app = null,
+        beginRotation = configuration.startRotation,
+        endRotation = configuration.endRotation,
+        app2 = null,
+        extraInfo = extraInfo)
+}
+
+/**
+ * Build a test tag for the test
  * @param testName Name of the transition(s) being tested
  * @param app App being launcher
  * @param configuration Configuration for the test
+ * @param extraInfo Additional information to append to the tag
  *
  * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
 </END_ROTATION></BEGIN_ROTATION></APP></NAME> */
@@ -121,14 +142,17 @@
  * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
 </EXTRA></NAME> */
 fun buildTestTag(
-    testName: String,
+    testName: String?,
     app: String?,
     beginRotation: Int,
     endRotation: Int,
     app2: String?,
     extraInfo: String
 ): String {
-    var testTag = testName
+    var testTag = ""
+    if (testName != null) {
+        testTag += testName
+    }
     if (app != null) {
         testTag += "__$app"
     }
@@ -142,5 +166,9 @@
     if (extraInfo.isNotEmpty()) {
         testTag += "__$extraInfo"
     }
+
+    if (testTag.startsWith("__")) {
+        testTag = testTag.drop(2)
+    }
     return testTag
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index fde97ba..c7736f8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -16,8 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -48,11 +46,9 @@
  * Test IME window closing back to app window transitions.
  * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
 class CloseImeAutoOpenWindowToAppTest(
     testSpec: FlickerTestRunnerFactory.TestSpec
 ) : FlickerTestRunner(testSpec) {
@@ -66,7 +62,7 @@
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
                     val testApp = ImeAppAutoFocusHelper(instrumentation,
                         configuration.startRotation)
-                    withTestName { buildTestTag("imeToAppAutoOpen", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -89,26 +85,39 @@
                         testApp.closeIME(device, wmHelper)
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry(listOf("InputMethod"))
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            imeAppWindowIsAlwaysVisible(testApp)
+                        postsubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                visibleWindowsShownMoreThanOneConsecutiveEntry(
+                                    listOf(IME_WINDOW_TITLE))
+                                imeAppWindowIsAlwaysVisible(testApp)
+                            }
+
+                            layersTrace {
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                noUncoveredRegions(configuration.startRotation)
+                                imeLayerBecomesInvisible()
+                                imeAppLayerIsAlwaysVisible(testApp)
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation)
+                                    statusBarLayerRotatesScales(configuration.startRotation)
+                                }
+                            }
                         }
 
-                        layersTrace {
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            noUncoveredRegions(configuration.startRotation)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(configuration.startRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            visibleLayersShownMoreThanOneConsecutiveEntry()
+                        flaky {
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
 
-                            imeLayerBecomesInvisible()
-                            imeAppLayerIsAlwaysVisible(testApp)
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation)
+                                    statusBarLayerRotatesScales(configuration.startRotation)
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index ab7c08f..aa24456 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -47,7 +46,6 @@
  * Test IME window closing back to app window transitions.
  * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -65,7 +63,7 @@
                     val testApp = ImeAppAutoFocusHelper(instrumentation,
                         configuration.startRotation)
                     withTestName {
-                        buildTestTag("imeToHomeAutoOpen", configuration)
+                        buildTestTag(configuration)
                     }
                     repeat { configuration.repetitions }
                     setup {
@@ -90,32 +88,50 @@
                         wmHelper.waitImeWindowGone()
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            imeWindowBecomesInvisible()
-                            imeAppWindowBecomesInvisible(testApp)
+                        presubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                visibleWindowsShownMoreThanOneConsecutiveEntry(
+                                    listOf(IME_WINDOW_TITLE))
+
+                                imeWindowBecomesInvisible()
+                                imeAppWindowBecomesInvisible(testApp)
+                            }
+
+                            layersTrace {
+                                noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
+                                imeLayerBecomesInvisible()
+                                imeAppLayerBecomesInvisible(testApp)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    navBarLayerIsAlwaysVisible()
+                                    statusBarLayerIsAlwaysVisible()
+                                    visibleLayersShownMoreThanOneConsecutiveEntry(
+                                        listOf(IME_WINDOW_TITLE))
+                                }
+                            }
                         }
 
-                        layersTrace {
-                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                Surface.ROTATION_0,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(configuration.startRotation,
-                                Surface.ROTATION_0,
-                                enabled = !configuration.startRotation.isRotated())
-                            navBarLayerIsAlwaysVisible(
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerIsAlwaysVisible(
-                                enabled = !configuration.startRotation.isRotated())
-                            visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE),
-                                enabled = !configuration.startRotation.isRotated())
-
-                            imeLayerBecomesInvisible()
-                            imeAppLayerBecomesInvisible(testApp)
+                        flaky {
+                            layersTrace {
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    navBarLayerIsAlwaysVisible()
+                                    statusBarLayerIsAlwaysVisible()
+                                    visibleLayersShownMoreThanOneConsecutiveEntry(
+                                        listOf(IME_WINDOW_TITLE))
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 0503cce0..2bd5abb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -16,8 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -47,11 +45,9 @@
  * Test IME window closing back to app window transitions.
  * To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
 class CloseImeWindowToAppTest(
     testSpec: FlickerTestRunnerFactory.TestSpec
 ) : FlickerTestRunner(testSpec) {
@@ -64,7 +60,7 @@
             val testApp = ImeAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
-                    withTestName { buildTestTag("imeToApp", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -86,24 +82,25 @@
                         testApp.closeIME(device, wmHelper)
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry(listOf("InputMethod"))
+                        postsubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                visibleWindowsShownMoreThanOneConsecutiveEntry(
+                                    listOf(IME_WINDOW_TITLE))
+                                imeAppWindowIsAlwaysVisible(testApp)
+                            }
 
-                            imeAppWindowIsAlwaysVisible(testApp)
-                        }
-
-                        layersTrace {
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            noUncoveredRegions(configuration.startRotation)
-                            navBarLayerRotatesAndScales(configuration.startRotation)
-                            statusBarLayerRotatesScales(configuration.startRotation)
-                            visibleLayersShownMoreThanOneConsecutiveEntry()
-
-                            imeLayerBecomesInvisible()
-                            imeAppLayerIsAlwaysVisible(testApp)
+                            layersTrace {
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                noUncoveredRegions(configuration.startRotation)
+                                navBarLayerRotatesAndScales(configuration.startRotation)
+                                statusBarLayerRotatesScales(configuration.startRotation)
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
+                                imeLayerBecomesInvisible()
+                                imeAppLayerIsAlwaysVisible(testApp)
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 9cb075b..7b61bb5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -16,8 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -47,11 +45,9 @@
  * Test IME window closing to home transitions.
  * To run this test: `atest FlickerTests:CloseImeWindowToHomeTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
 class CloseImeWindowToHomeTest(
     testSpec: FlickerTestRunnerFactory.TestSpec
 ) : FlickerTestRunner(testSpec) {
@@ -63,7 +59,7 @@
             val testApp = ImeAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
-                    withTestName { buildTestTag("imeToHome", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -91,31 +87,47 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            imeWindowBecomesInvisible()
-                            imeAppWindowBecomesInvisible(testApp)
+                        postsubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                visibleWindowsShownMoreThanOneConsecutiveEntry(
+                                    listOf(IME_WINDOW_TITLE))
+                                imeWindowBecomesInvisible()
+                                imeAppWindowBecomesInvisible(testApp)
+                            }
+
+                            layersTrace {
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                imeLayerBecomesInvisible()
+                                imeAppLayerBecomesInvisible(testApp)
+                                noUncoveredRegions(configuration.startRotation,
+                                    Surface.ROTATION_0)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                }
+                            }
                         }
 
-                        layersTrace {
-                            noUncoveredRegions(configuration.startRotation,
-                                Surface.ROTATION_0)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                Surface.ROTATION_0,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(configuration.startRotation,
-                                Surface.ROTATION_0,
-                                enabled = !configuration.startRotation.isRotated())
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE),
-                                enabled = false)
+                        flaky {
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry(
+                                    listOf(IME_WINDOW_TITLE))
 
-                            imeLayerBecomesInvisible()
-                            imeAppLayerBecomesInvisible(testApp)
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                    statusBarLayerRotatesScales(configuration.startRotation,
+                                        Surface.ROTATION_0)
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index c775cb8..cfdd856 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -17,106 +17,82 @@
 package com.android.server.wm.flicker.ime
 
 import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
-import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
+import com.android.server.wm.flicker.dsl.WmAssertionBuilder
 
 const val IME_WINDOW_TITLE = "InputMethod"
 
 @JvmOverloads
-fun LayersAssertionBuilderLegacy.imeLayerBecomesVisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeLayerBecomesVisible", bugId, enabled) {
+fun LayersAssertionBuilder.imeLayerBecomesVisible(bugId: Int = 0) {
+    all("imeLayerBecomesVisible", bugId) {
         this.hidesLayer(IME_WINDOW_TITLE)
                 .then()
                 .showsLayer(IME_WINDOW_TITLE)
     }
 }
 
-fun LayersAssertionBuilderLegacy.imeLayerBecomesInvisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeLayerBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun LayersAssertionBuilder.imeLayerBecomesInvisible(bugId: Int = 0) {
+    all("imeLayerBecomesInvisible", bugId) {
         this.showsLayer(IME_WINDOW_TITLE)
                 .then()
                 .hidesLayer(IME_WINDOW_TITLE)
     }
 }
 
-fun LayersAssertionBuilderLegacy.imeAppLayerIsAlwaysVisible(
-    testApp: IAppHelper,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeAppLayerIsAlwaysVisible", bugId, enabled) {
+@JvmOverloads
+fun LayersAssertionBuilder.imeAppLayerIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) {
+    all("imeAppLayerIsAlwaysVisible", bugId) {
         this.showsLayer(testApp.getPackage())
     }
 }
 
-fun WmAssertionBuilderLegacy.imeAppWindowIsAlwaysVisible(
-    testApp: IAppHelper,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeAppWindowIsAlwaysVisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeAppWindowIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) {
+    all("imeAppWindowIsAlwaysVisible", bugId) {
         this.showsAppWindowOnTop(testApp.getPackage())
     }
 }
 
-fun WmAssertionBuilderLegacy.imeWindowBecomesVisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeWindowBecomesVisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeWindowBecomesVisible(bugId: Int = 0) {
+    all("imeWindowBecomesVisible", bugId) {
         this.hidesNonAppWindow(IME_WINDOW_TITLE)
                 .then()
                 .showsNonAppWindow(IME_WINDOW_TITLE)
     }
 }
 
-fun WmAssertionBuilderLegacy.imeWindowBecomesInvisible(
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeWindowBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeWindowBecomesInvisible(bugId: Int = 0) {
+    all("imeWindowBecomesInvisible", bugId) {
         this.showsNonAppWindow(IME_WINDOW_TITLE)
                 .then()
                 .hidesNonAppWindow(IME_WINDOW_TITLE)
     }
 }
 
-fun WmAssertionBuilderLegacy.imeAppWindowBecomesVisible(
-    windowName: String,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeAppWindowBecomesVisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeAppWindowBecomesVisible(windowName: String, bugId: Int = 0) {
+    all("imeAppWindowBecomesVisible", bugId) {
         this.hidesAppWindow(windowName)
                 .then()
                 .showsAppWindow(windowName)
     }
 }
 
-fun WmAssertionBuilderLegacy.imeAppWindowBecomesInvisible(
-    testApp: IAppHelper,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeAppWindowBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeAppWindowBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) {
+    all("imeAppWindowBecomesInvisible", bugId) {
         this.showsAppWindowOnTop(testApp.getPackage())
                 .then()
                 .appWindowNotOnTop(testApp.getPackage())
     }
 }
 
-fun LayersAssertionBuilderLegacy.imeAppLayerBecomesInvisible(
-    testApp: IAppHelper,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("imeAppLayerBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun LayersAssertionBuilder.imeAppLayerBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) {
+    all("imeAppLayerBecomesInvisible", bugId) {
         this.skipUntilFirstAssertion()
                 .showsLayer(testApp.getPackage())
                 .then()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 13c6cd7..9e94d6e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -16,8 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -50,11 +48,9 @@
  * Test IME window opening transitions.
  * To run this test: `atest FlickerTests:OpenImeWindowTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
 class OpenImeWindowTest(
     testSpec: FlickerTestRunnerFactory.TestSpec
 ) : FlickerTestRunner(testSpec) {
@@ -66,7 +62,7 @@
             val testApp = ImeAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
-                    withTestName { buildTestTag("openIme", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -88,27 +84,43 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry()
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            imeWindowBecomesVisible()
-                            appWindowAlwaysVisibleOnTop(testApp.`package`)
+                        postsubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+
+                                imeWindowBecomesVisible()
+                                appWindowAlwaysVisibleOnTop(testApp.`package`)
+                            }
+
+                            layersTrace {
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                noUncoveredRegions(configuration.startRotation)
+                                imeLayerBecomesVisible()
+                                layerAlwaysVisible(testApp.`package`)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation)
+                                    statusBarLayerRotatesScales(configuration.startRotation)
+                                }
+                            }
                         }
 
-                        layersTrace {
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            noUncoveredRegions(configuration.startRotation)
-                            navBarLayerRotatesAndScales(configuration.startRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(configuration.startRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            visibleLayersShownMoreThanOneConsecutiveEntry()
+                        flaky {
+                            windowManagerTrace {
+                                visibleWindowsShownMoreThanOneConsecutiveEntry()
+                            }
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
 
-                            imeLayerBecomesVisible()
-                            layerAlwaysVisible(testApp.`package`)
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(configuration.startRotation)
+                                    statusBarLayerRotatesScales(configuration.startRotation)
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 6cc64df..2fe49af 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -52,7 +51,6 @@
  * Test IME window opening transitions.
  * To run this test: `atest FlickerTests:ReOpenImeWindowTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -67,34 +65,37 @@
             val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 1) { configuration ->
-                        val testApp = ImeAppAutoFocusHelper(instrumentation,
-                            configuration.startRotation)
-                        withTestName { buildTestTag("reOpenImeAutoFocus", configuration) }
-                        repeat { configuration.repetitions }
-                        setup {
-                            test {
-                                device.wakeUpAndGoToHomeScreen()
-                                testApp.launchViaIntent(wmHelper)
-                                testApp.openIME(device, wmHelper)
-                            }
-                            eachRun {
-                                device.pressRecentApps()
-                                wmHelper.waitImeWindowGone()
-                                wmHelper.waitForAppTransitionIdle()
-                                this.setRotation(configuration.startRotation)
-                            }
+                    val testApp = ImeAppAutoFocusHelper(instrumentation,
+                        configuration.startRotation)
+                    withTestName { buildTestTag(configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            testApp.launchViaIntent(wmHelper)
+                            testApp.openIME(device, wmHelper)
                         }
-                        transitions {
-                            device.reopenAppFromOverview()
-                            wmHelper.waitImeWindowShown()
+                        eachRun {
+                            device.pressRecentApps()
+                            wmHelper.waitImeWindowGone()
+                            wmHelper.waitForAppTransitionIdle()
+                            this.setRotation(configuration.startRotation)
                         }
-                        teardown {
-                            test {
-                                this.setRotation(Surface.ROTATION_0)
-                                testApp.exit()
-                            }
+                    }
+                    transitions {
+                        device.reopenAppFromOverview()
+                        wmHelper.waitImeWindowShown()
+                    }
+                    teardown {
+                        test {
+                            this.setRotation(Surface.ROTATION_0)
+                            testApp.exit()
                         }
-                        assertions {
+                    }
+                    assertions {
+                        val isRotated = configuration.startRotation.isRotated()
+
+                        presubmit {
                             windowManagerTrace {
                                 navBarWindowIsAlwaysVisible()
                                 statusBarWindowIsAlwaysVisible()
@@ -107,21 +108,34 @@
 
                             layersTrace {
                                 noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
-                                navBarLayerRotatesAndScales(Surface.ROTATION_0,
-                                        configuration.endRotation,
-                                        enabled = !configuration.startRotation.isRotated())
-                                statusBarLayerRotatesScales(Surface.ROTATION_0,
-                                        configuration.endRotation,
-                                        enabled = !configuration.startRotation.isRotated())
                                 statusBarLayerIsAlwaysVisible()
                                 navBarLayerIsAlwaysVisible()
-                                visibleLayersShownMoreThanOneConsecutiveEntry(enabled = false)
-
                                 imeLayerBecomesVisible()
                                 appLayerReplacesWallpaperLayer(testAppComponentName.className)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                }
+                            }
+                        }
+
+                        flaky {
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
+
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                }
                             }
                         }
                     }
+                }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
index 1bd1190..be3fa5f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
@@ -17,14 +17,10 @@
 package com.android.server.wm.flicker.launch
 
 import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.WmAssertionBuilder
 
-fun WmAssertionBuilderLegacy.appWindowReplacesLauncherAsTopWindow(
-    testApp: IAppHelper,
-    bugId: Int = 0,
-    enabled: Boolean = bugId == 0
-) {
-    all("appWindowReplacesLauncherAsTopWindow", bugId, enabled) {
+fun WmAssertionBuilder.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper, bugId: Int = 0) {
+    all("appWindowReplacesLauncherAsTopWindow", bugId) {
         this.showsAppWindowOnTop("Launcher")
                 .then()
                 .showsAppWindowOnTop("Snapshot", testApp.getPackage())
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 010ebf2..0ec0b04 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -51,7 +50,6 @@
  * Test cold launch app from launcher.
  * To run this test: `atest FlickerTests:OpenAppColdTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -66,7 +64,7 @@
             val testApp = SimpleAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation) { configuration ->
-                    withTestName { buildTestTag("openAppCold", testApp, configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -88,33 +86,48 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry()
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            appWindowReplacesLauncherAsTopWindow(testApp)
-                            wallpaperWindowBecomesInvisible()
+                        presubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                visibleWindowsShownMoreThanOneConsecutiveEntry()
+                                appWindowReplacesLauncherAsTopWindow(testApp)
+                                wallpaperWindowBecomesInvisible()
+                            }
+
+                            layersTrace {
+                                // During testing the launcher is always in portrait mode
+                                noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                appLayerReplacesWallpaperLayer(testApp.`package`)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                }
+                            }
+
+                            eventLog {
+                                focusChanges("NexusLauncherActivity", testApp.`package`)
+                            }
                         }
 
-                        layersTrace {
-                            // During testing the launcher is always in portrait mode
-                            noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
-                            navBarLayerRotatesAndScales(Surface.ROTATION_0,
-                                configuration.endRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(Surface.ROTATION_0,
-                                configuration.endRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            visibleLayersShownMoreThanOneConsecutiveEntry(enabled = false)
+                        flaky {
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
 
-                            appLayerReplacesWallpaperLayer(testApp.`package`)
-                        }
-
-                        eventLog {
-                            focusChanges("NexusLauncherActivity", testApp.`package`)
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 5e08921..84cc8e3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -52,7 +51,6 @@
  * Launch an app from the recents app view (the overview)
  * To run this test: `atest FlickerTests:OpenAppFromOverviewTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -67,7 +65,7 @@
             val testApp = SimpleAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation, repetitions = 5) { configuration ->
-                    withTestName { buildTestTag("openAppFromOverview", configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -92,36 +90,57 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry()
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            appWindowReplacesLauncherAsTopWindow(testApp)
-                            wallpaperWindowBecomesInvisible()
+                        presubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                appWindowReplacesLauncherAsTopWindow(testApp)
+                                wallpaperWindowBecomesInvisible()
+                            }
+
+                            layersTrace {
+                                appLayerReplacesWallpaperLayer(testApp.`package`)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                } else {
+                                    statusBarLayerIsAlwaysVisible()
+                                    navBarLayerIsAlwaysVisible()
+                                }
+                            }
+
+                            eventLog {
+                                focusChanges("NexusLauncherActivity", testApp.`package`)
+                            }
                         }
 
-                        layersTrace {
-                            noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
-                                bugId = 141361128)
-                            navBarLayerRotatesAndScales(Surface.ROTATION_0,
-                                configuration.endRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(Surface.ROTATION_0,
-                                configuration.endRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerIsAlwaysVisible(
-                                enabled = Surface.ROTATION_0 == configuration.endRotation)
-                            navBarLayerIsAlwaysVisible(
-                                enabled = Surface.ROTATION_0 == configuration.endRotation)
-                            visibleLayersShownMoreThanOneConsecutiveEntry(
-                                enabled = false)
-
-                            appLayerReplacesWallpaperLayer(testApp.`package`)
+                        postsubmit {
+                            windowManagerTrace {
+                                visibleWindowsShownMoreThanOneConsecutiveEntry()
+                            }
                         }
 
-                        eventLog {
-                            focusChanges("NexusLauncherActivity", testApp.`package`)
+                        flaky {
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
+                                noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
+                                    bugId = 141361128)
+
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                } else {
+                                    statusBarLayerIsAlwaysVisible()
+                                    navBarLayerIsAlwaysVisible()
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index 092cd4d..1f375a5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
@@ -51,7 +50,6 @@
  * Test warm launch app.
  * To run this test: `atest FlickerTests:OpenAppWarmTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -64,7 +62,7 @@
             val testApp = SimpleAppHelper(instrumentation)
             return FlickerTestRunnerFactory.getInstance()
                 .buildTest(instrumentation) { configuration ->
-                    withTestName { buildTestTag("openAppWarm", testApp, configuration) }
+                    withTestName { buildTestTag(configuration) }
                     repeat { configuration.repetitions }
                     setup {
                         test {
@@ -91,33 +89,49 @@
                         }
                     }
                     assertions {
-                        windowManagerTrace {
-                            navBarWindowIsAlwaysVisible()
-                            statusBarWindowIsAlwaysVisible()
-                            visibleWindowsShownMoreThanOneConsecutiveEntry()
+                        val isRotated = configuration.startRotation.isRotated()
 
-                            appWindowReplacesLauncherAsTopWindow(testApp)
-                            wallpaperWindowBecomesInvisible()
+                        presubmit {
+                            windowManagerTrace {
+                                navBarWindowIsAlwaysVisible()
+                                statusBarWindowIsAlwaysVisible()
+                                visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+                                appWindowReplacesLauncherAsTopWindow(testApp)
+                                wallpaperWindowBecomesInvisible()
+                            }
+
+                            layersTrace {
+                                // During testing the launcher is always in portrait mode
+                                noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
+                                navBarLayerIsAlwaysVisible()
+                                statusBarLayerIsAlwaysVisible()
+                                appLayerReplacesWallpaperLayer(testApp.`package`)
+
+                                if (!isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                }
+                            }
+
+                            eventLog {
+                                focusChanges("NexusLauncherActivity", testApp.`package`)
+                            }
                         }
 
-                        layersTrace {
-                            // During testing the launcher is always in portrait mode
-                            noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
-                            navBarLayerRotatesAndScales(Surface.ROTATION_0,
-                                configuration.endRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            statusBarLayerRotatesScales(Surface.ROTATION_0,
-                                configuration.endRotation,
-                                enabled = !configuration.startRotation.isRotated())
-                            navBarLayerIsAlwaysVisible()
-                            statusBarLayerIsAlwaysVisible()
-                            visibleLayersShownMoreThanOneConsecutiveEntry(enabled = false)
+                        flaky {
+                            layersTrace {
+                                visibleLayersShownMoreThanOneConsecutiveEntry()
 
-                            appLayerReplacesWallpaperLayer(testApp.`package`)
-                        }
-
-                        eventLog {
-                            focusChanges("NexusLauncherActivity", testApp.`package`)
+                                if (isRotated) {
+                                    navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                    statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                        configuration.endRotation)
+                                }
+                            }
                         }
                     }
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 1c44b21..7bfdd96 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.rotation
 
 import android.os.Bundle
-import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerTestRunner
@@ -48,7 +47,6 @@
  * Cycle through supported app rotations.
  * To run this test: `atest FlickerTests:ChangeAppRotationTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -67,51 +65,56 @@
         @JvmStatic
         fun getParams(): Collection<Array<Any>> {
             val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
-                withTestName { buildTestTag("changeAppRotation", configuration) }
+                withTestName { buildTestTag(configuration) }
                 assertions {
-                    windowManagerTrace {
-                        navBarWindowIsAlwaysVisible()
-                        statusBarWindowIsAlwaysVisible()
-                        visibleWindowsShownMoreThanOneConsecutiveEntry()
-                    }
-
-                    layersTrace {
-                        navBarLayerIsAlwaysVisible(bugId = 140855415)
-                        statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                        noUncoveredRegions(configuration.startRotation,
-                            configuration.endRotation, allStates = false)
-                        navBarLayerRotatesAndScales(configuration.startRotation,
-                            configuration.endRotation, bugId = 140855415)
-                        statusBarLayerRotatesScales(configuration.startRotation,
-                            configuration.endRotation, bugId = 140855415)
-                        visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 140855415)
-                    }
-
-                    layersTrace {
-                        val startingPos = WindowUtils.getDisplayBounds(
-                            configuration.startRotation)
-                        val endingPos = WindowUtils.getDisplayBounds(
-                            configuration.endRotation)
-
-                        start("appLayerRotates_StartingPos", bugId = 140855415) {
-                            this.hasVisibleRegion(testApp.getPackage(), startingPos)
+                    presubmit {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            visibleWindowsShownMoreThanOneConsecutiveEntry()
                         }
 
-                        end("appLayerRotates_EndingPos", bugId = 140855415) {
-                            this.hasVisibleRegion(testApp.getPackage(), endingPos)
-                        }
+                        layersTrace {
+                            noUncoveredRegions(configuration.startRotation,
+                                configuration.endRotation, allStates = false)
 
-                        all("screenshotLayerBecomesInvisible") {
-                            this.showsLayer(testApp.getPackage())
-                                .then()
-                                .showsLayer(SCREENSHOT_LAYER)
-                                .then()
-                                .showsLayer(testApp.getPackage())
+                            all("screenshotLayerBecomesInvisible") {
+                                this.showsLayer(testApp.getPackage())
+                                    .then()
+                                    .showsLayer(SCREENSHOT_LAYER)
+                                    .then()
+                                    .showsLayer(testApp.getPackage())
+                            }
                         }
                     }
 
-                    eventLog {
-                        focusDoesNotChange(bugId = 151179149)
+                    flaky {
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                configuration.endRotation, bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                configuration.endRotation, bugId = 140855415)
+                            visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 140855415)
+
+                            val startingPos = WindowUtils.getDisplayBounds(
+                                configuration.startRotation)
+                            val endingPos = WindowUtils.getDisplayBounds(
+                                configuration.endRotation)
+
+                            start("appLayerRotates_StartingPos", bugId = 140855415) {
+                                this.hasVisibleRegion(testApp.getPackage(), startingPos)
+                            }
+
+                            end("appLayerRotates_EndingPos", bugId = 140855415) {
+                                this.hasVisibleRegion(testApp.getPackage(), endingPos)
+                            }
+                        }
+
+                        eventLog {
+                            focusDoesNotChange(bugId = 151179149)
+                        }
                     }
                 }
             }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index f04131b..7861464 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.rotation
 
 import android.os.Bundle
-import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerTestRunner
@@ -51,7 +50,6 @@
  * Cycle through supported app rotations using seamless rotations.
  * To run this test: `atest FlickerTests:SeamlessAppRotationTest`
  */
-@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -97,63 +95,67 @@
                     } else {
                         ""
                     }
-                    buildTestTag("seamlessRotation", configuration, extraInfo = extra)
+                    buildTestTag(configuration, extraInfo = extra)
                 }
                 assertions {
-                    windowManagerTrace {
-                        navBarWindowIsAlwaysVisible(bugId = 140855415)
-                        statusBarWindowIsAlwaysVisible(bugId = 140855415)
-                        visibleWindowsShownMoreThanOneConsecutiveEntry()
-                        appWindowAlwaysVisibleOnTop(testApp.`package`)
-                    }
+                    val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation)
+                    val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation)
 
-                    layersTrace {
-                        navBarLayerIsAlwaysVisible(bugId = 140855415)
-                        statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                        noUncoveredRegions(configuration.startRotation,
-                            configuration.endRotation, allStates = false, bugId = 147659548)
-                        navBarLayerRotatesAndScales(configuration.startRotation,
-                            configuration.endRotation,
-                            enabled = false)
-                        statusBarLayerRotatesScales(configuration.startRotation,
-                            configuration.endRotation, enabled = false)
-                        visibleLayersShownMoreThanOneConsecutiveEntry(
-                                enabled = configuration.startRotation == configuration.endRotation)
-                        layerAlwaysVisible(testApp.`package`)
-                    }
-
-                    layersTrace {
-                        val startingBounds = WindowUtils
-                            .getDisplayBounds(configuration.startRotation)
-                        val endingBounds = WindowUtils
-                            .getDisplayBounds(configuration.endRotation)
-
-                        all("appLayerRotates", bugId = 147659548) {
-                            if (startingBounds == endingBounds) {
-                                this.hasVisibleRegion(
-                                    testApp.`package`, startingBounds)
-                            } else {
-                                this.hasVisibleRegion(testApp.`package`,
-                                    startingBounds)
-                                    .then()
-                                    .hasVisibleRegion(testApp.`package`,
-                                        endingBounds)
-                            }
+                    presubmit {
+                        windowManagerTrace {
+                            visibleWindowsShownMoreThanOneConsecutiveEntry()
+                            appWindowAlwaysVisibleOnTop(testApp.`package`)
                         }
 
-                        all("noUncoveredRegions", bugId = 147659548) {
-                            if (startingBounds == endingBounds) {
-                                this.coversAtLeastRegion(startingBounds)
-                            } else {
-                                this.coversAtLeastRegion(startingBounds)
-                                    .then()
-                                    .coversAtLeastRegion(endingBounds)
-                            }
+                        layersTrace {
+                            layerAlwaysVisible(testApp.`package`)
                         }
                     }
 
-                    eventLog {
-                        focusDoesNotChange(bugId = 151179149)
+                    flaky {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible(bugId = 140855415)
+                            statusBarWindowIsAlwaysVisible(bugId = 140855415)
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                            noUncoveredRegions(configuration.startRotation,
+                                configuration.endRotation, allStates = false, bugId = 147659548)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                configuration.endRotation)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                configuration.endRotation)
+                            visibleLayersShownMoreThanOneConsecutiveEntry()
+
+                            all("appLayerRotates", bugId = 147659548) {
+                                if (startingBounds == endingBounds) {
+                                    this.hasVisibleRegion(
+                                        testApp.`package`, startingBounds)
+                                } else {
+                                    this.hasVisibleRegion(testApp.`package`,
+                                        startingBounds)
+                                        .then()
+                                        .hasVisibleRegion(testApp.`package`,
+                                            endingBounds)
+                                }
+                            }
+
+                            all("noUncoveredRegions", bugId = 147659548) {
+                                if (startingBounds == endingBounds) {
+                                    this.coversAtLeastRegion(startingBounds)
+                                } else {
+                                    this.coversAtLeastRegion(startingBounds)
+                                        .then()
+                                        .coversAtLeastRegion(endingBounds)
+                                }
+                            }
+                        }
+
+                        eventLog {
+                            focusDoesNotChange(bugId = 151179149)
+                        }
                     }
                 }
             }
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 1a940c7..c6c67fe 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -753,6 +753,15 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="RenderEffectShaderActivity"
+                  android:label="RenderEffect/Shader"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="com.android.test.hwui.TEST"/>
+            </intent-filter>
+        </activity>
+
         <activity android:name="TextActivity"
              android:label="Text/Simple Text"
              android:theme="@android:style/Theme.NoTitleBar"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java
new file mode 100644
index 0000000..661d48a
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.RenderEffect;
+import android.graphics.RenderNode;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class RenderEffectShaderActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        LinearLayout layout = new LinearLayout(this);
+        layout.setClipChildren(false);
+        layout.setGravity(Gravity.CENTER);
+        layout.setOrientation(LinearLayout.VERTICAL);
+
+
+        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(500, 500);
+        params.bottomMargin = 100;
+
+        layout.addView(new ShaderRenderEffectView(this), params);
+
+        setContentView(layout);
+    }
+
+    public static class ShaderRenderEffectView extends View {
+
+        private final Paint mPaint;
+        private final RenderNode mRenderNode;
+
+        public ShaderRenderEffectView(Context c) {
+            super(c);
+
+            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            mRenderNode = new RenderNode("blurNode");
+
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+            if (changed) {
+                LinearGradient gradient = new LinearGradient(
+                        0f, 0f,
+                        0f, bottom - top,
+                        new int[]{Color.CYAN, Color.MAGENTA},
+                        null,
+                        Shader.TileMode.CLAMP
+                );
+                mRenderNode.setRenderEffect(
+                        RenderEffect.createShaderEffect(gradient)
+                );
+
+                int width = right - left;
+                int height = bottom - top;
+                mRenderNode.setPosition(0, 0, width, height);
+                Canvas canvas = mRenderNode.beginRecording(width, height);
+                mPaint.setColor(Color.BLUE);
+
+                canvas.drawRect(
+                        0,
+                        0,
+                        width,
+                        height,
+                        mPaint
+                );
+
+                mPaint.setColor(Color.RED);
+                canvas.drawCircle((right - left) / 2f, (bottom - top) / 2f, 50f, mPaint);
+
+                mRenderNode.endRecording();
+            }
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRenderNode(mRenderNode);
+        }
+    }
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
index 9b15b04..5c26448 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
@@ -34,12 +34,14 @@
     constructor(context: Context) : this(context, null)
     constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
         displayManager = context.getSystemService(DisplayManager::class.java)!!
+        displayId = context.getDisplayId()
     }
 
     private var window: Window? = null
     private var currentModeDisplay: TextView? = null
     private val displayManager: DisplayManager
     private var targetSdrWhitePointIndex = 0
+    private var displayId: Int
 
     private val whitePoint get() = SDR_WHITE_POINTS[targetSdrWhitePointIndex]
 
@@ -107,7 +109,7 @@
         // Imperfect, but close enough, synchronization by waiting for frame commit to set the value
         viewTreeObserver.registerFrameCommitCallback {
             try {
-                displayManager.setTemporaryBrightness(level)
+                displayManager.setTemporaryBrightness(displayId, level)
             } catch (ex: Exception) {
                 // Ignore a permission denied rejection - it doesn't meaningfully change much
             }
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index dc9e587..e1da3d0 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -84,6 +85,7 @@
         final String typeName = ConnectivityManager.getNetworkTypeName(type);
         mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities();
         mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         mNetworkCapabilities.addTransportType(transport);
         switch (transport) {
             case TRANSPORT_ETHERNET:
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index c2fddf3..fcfb4aa 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -345,15 +345,17 @@
     @Test
     public void testRequestType() throws Exception {
         final String testPkgName = "MyPackage";
+        final String testAttributionTag = "MyTag";
         final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
         when(mCtx.getOpPackageName()).thenReturn(testPkgName);
+        when(mCtx.getAttributionTag()).thenReturn(testAttributionTag);
         final NetworkRequest request = makeRequest(1);
         final NetworkCallback callback = new ConnectivityManager.NetworkCallback();
 
         manager.requestNetwork(request, callback);
         verify(mService).requestNetwork(eq(request.networkCapabilities),
                 eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
-                eq(testPkgName), eq(null));
+                eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         // Verify that register network callback does not calls requestNetwork at all.
@@ -361,19 +363,19 @@
         verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(),
                 anyInt(), any(), any());
         verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(),
-                eq(testPkgName));
+                eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         manager.registerDefaultNetworkCallback(callback);
         verify(mService).requestNetwork(eq(null),
                 eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
-                eq(testPkgName), eq(null));
+                eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         manager.requestBackgroundNetwork(request, null, callback);
         verify(mService).requestNetwork(eq(request.networkCapabilities),
                 eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
-                eq(testPkgName), eq(null));
+                eq(testPkgName), eq(testAttributionTag));
         reset(mService);
     }
 
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
index 91fcbc0..1f8f6f3 100644
--- a/tests/net/java/android/net/NetworkTemplateTest.kt
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -35,7 +35,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.mock
 import org.mockito.MockitoAnnotations
 import kotlin.test.assertFalse
@@ -60,16 +59,13 @@
         subscriberId: String? = null,
         ssid: String? = null
     ): NetworkState {
-        val info = mock(NetworkInfo::class.java)
-        doReturn(type).`when`(info).type
-        doReturn(NetworkInfo.State.CONNECTED).`when`(info).state
         val lp = LinkProperties()
         val caps = NetworkCapabilities().apply {
             setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
             setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
             setSSID(ssid)
         }
-        return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid)
+        return NetworkState(type, lp, caps, mock(Network::class.java), subscriberId, ssid)
     }
 
     private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) =
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index bcbc9e0..bf61634 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2707,6 +2707,10 @@
 
         NetworkCapabilities filter = new NetworkCapabilities();
         filter.addCapability(capability);
+        // Add NOT_VCN_MANAGED capability into filter unconditionally since some request will add
+        // NOT_VCN_MANAGED automatically but not for NetworkCapabilities,
+        // see {@code NetworkCapabilities#deduceNotVcnManagedCapability} for more details.
+        filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
         handlerThread.start();
         final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
@@ -3958,6 +3962,7 @@
         handlerThread.start();
         NetworkCapabilities filter = new NetworkCapabilities()
                 .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .addCapability(NET_CAPABILITY_INTERNET);
         final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
                 mServiceContext, "testFactory", filter);
@@ -5861,6 +5866,7 @@
                 .addTransportType(TRANSPORT_CELLULAR)
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .setLinkDownstreamBandwidthKbps(10);
         final NetworkCapabilities wifiNc = new NetworkCapabilities()
                 .addTransportType(TRANSPORT_WIFI)
@@ -5869,6 +5875,7 @@
                 .addCapability(NET_CAPABILITY_NOT_ROAMING)
                 .addCapability(NET_CAPABILITY_NOT_CONGESTED)
                 .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .setLinkUpstreamBandwidthKbps(20);
         mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */);
         mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */);
@@ -6365,7 +6372,7 @@
 
     private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) {
         final NetworkCapabilities[] defaultCaps = mService.getDefaultNetworkCapabilitiesForUser(
-                userId, "com.android.calling.package");
+                userId, "com.android.calling.package", "com.test");
         final String defaultCapsString = Arrays.toString(defaultCaps);
         assertEquals(defaultCapsString, defaultCaps.length, networks.length);
         final Set<NetworkCapabilities> defaultCapsSet = new ArraySet<>(defaultCaps);
@@ -7514,19 +7521,13 @@
             mWiFiNetworkAgent.removeCapability(testCap);
             callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent);
             callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent);
-            // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has
-            //  it.
-            if (testCap == NET_CAPABILITY_TRUSTED) {
-                verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
-                reset(mMockNetd);
-            }
+            verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+            reset(mMockNetd);
 
             mCellNetworkAgent.removeCapability(testCap);
             callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
             callbackWithoutCap.assertNoCallback();
-            if (testCap == NET_CAPABILITY_TRUSTED) {
-                verify(mMockNetd).networkClearDefault();
-            }
+            verify(mMockNetd).networkClearDefault();
 
             mCm.unregisterNetworkCallback(callbackWithCap);
             mCm.unregisterNetworkCallback(callbackWithoutCap);
@@ -8377,7 +8378,8 @@
         when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
 
         if (op != null) {
-            when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
+            when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()),
+                    eq(mContext.getPackageName()), eq(getAttributionTag()), anyString()))
                 .thenReturn(AppOpsManager.MODE_ALLOWED);
         }
 
@@ -8390,7 +8392,7 @@
         final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
 
         return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName()).getOwnerUid();
+                netCap, callerUid, mContext.getPackageName(), getAttributionTag()).getOwnerUid();
     }
 
     private void verifyWifiInfoCopyNetCapsForCallerPermission(
@@ -8400,7 +8402,7 @@
         final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo);
 
         mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName());
+                netCap, callerUid, mContext.getPackageName(), getAttributionTag());
         verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable));
     }
 
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index dde78aa..214c82d 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -80,8 +80,6 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkState;
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
@@ -1456,8 +1454,6 @@
     }
 
     private static NetworkState buildWifiState(boolean isMetered, @NonNull String iface) {
-        final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
-        info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(iface);
         final NetworkCapabilities capabilities = new NetworkCapabilities();
@@ -1465,7 +1461,7 @@
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
         capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
         capabilities.setSSID(TEST_SSID);
-        return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
+        return new NetworkState(TYPE_WIFI, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
     }
 
     private static NetworkState buildMobile3gState(String subscriberId) {
@@ -1473,17 +1469,14 @@
     }
 
     private static NetworkState buildMobile3gState(String subscriberId, boolean isRoaming) {
-        final NetworkInfo info = new NetworkInfo(
-                TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UMTS, null, null);
-        info.setDetailedState(DetailedState.CONNECTED, null, null);
-        info.setRoaming(isRoaming);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
         final NetworkCapabilities capabilities = new NetworkCapabilities();
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
         capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-        return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
+        return new NetworkState(
+                TYPE_MOBILE, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
     }
 
     private NetworkStats buildEmptyStats() {
@@ -1491,11 +1484,9 @@
     }
 
     private static NetworkState buildVpnState() {
-        final NetworkInfo info = new NetworkInfo(TYPE_VPN, 0, null, null);
-        info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TUN_IFACE);
-        return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);
+        return new NetworkState(TYPE_VPN, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);
     }
 
     private long getElapsedRealtime() {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index e20070e..278d93a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -18,22 +18,35 @@
 
 import static android.net.IpSecManager.DIRECTION_IN;
 import static android.net.IpSecManager.DIRECTION_OUT;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
+import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
 import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Collections;
 
 /** Tests for VcnGatewayConnection.ConnectedState */
 @RunWith(AndroidJUnit4.class)
@@ -107,6 +120,51 @@
     }
 
     @Test
+    public void testChildOpenedRegistersNetwork() throws Exception {
+        final VcnChildSessionConfiguration mMockChildSessionConfig =
+                mock(VcnChildSessionConfiguration.class);
+        doReturn(Collections.singletonList(TEST_INTERNAL_ADDR))
+                .when(mMockChildSessionConfig)
+                .getInternalAddresses();
+        doReturn(Collections.singletonList(TEST_DNS_ADDR))
+                .when(mMockChildSessionConfig)
+                .getInternalDnsServers();
+
+        getChildSessionCallback().onOpened(mMockChildSessionConfig);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+
+        final ArgumentCaptor<LinkProperties> lpCaptor =
+                ArgumentCaptor.forClass(LinkProperties.class);
+        final ArgumentCaptor<NetworkCapabilities> ncCaptor =
+                ArgumentCaptor.forClass(NetworkCapabilities.class);
+        verify(mConnMgr)
+                .registerNetworkAgent(
+                        any(),
+                        any(),
+                        lpCaptor.capture(),
+                        ncCaptor.capture(),
+                        anyInt(),
+                        any(),
+                        anyInt());
+        verify(mIpSecSvc)
+                .addAddressToTunnelInterface(
+                        eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any());
+
+        final LinkProperties lp = lpCaptor.getValue();
+        assertEquals(Collections.singletonList(TEST_INTERNAL_ADDR), lp.getLinkAddresses());
+        assertEquals(Collections.singletonList(TEST_DNS_ADDR), lp.getDnsServers());
+
+        final NetworkCapabilities nc = ncCaptor.getValue();
+        assertTrue(nc.hasTransport(TRANSPORT_CELLULAR));
+        assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+        for (int cap : mConfig.getAllExposedCapabilities()) {
+            assertTrue(nc.hasCapability(cap));
+        }
+    }
+
+    @Test
     public void testChildSessionClosedTriggersDisconnect() throws Exception {
         getChildSessionCallback().onClosed();
         mTestLooper.dispatchAll();
@@ -122,6 +180,4 @@
         assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
         verify(mIkeSession).close();
     }
-
-    // TODO: Add tests for childOpened() when ChildSessionConfiguration can be mocked or created
 }
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
new file mode 100644
index 0000000..3f2b47c
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 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.vcn;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.RetryTimeoutState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnectionTestBase {
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
+        mGatewayConnection.transitionTo(mGatewayConnection.mRetryTimeoutState);
+        mTestLooper.dispatchAll();
+    }
+
+    @Test
+    public void testNewNetworkTriggerRetry() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testSameNetworkDoesNotTriggerRetry() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testNullNetworkTriggersDisconnect() throws Exception {
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(null);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testTimeoutElapsingTriggersRetry() throws Exception {
+        mTestLooper.moveTimeForward(mConfig.getRetryIntervalsMs()[0]);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+    }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 333b5b9..d449eab 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -27,10 +27,13 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.InetAddresses;
 import android.net.IpSecConfig;
 import android.net.IpSecManager;
 import android.net.IpSecTransform;
 import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
@@ -44,15 +47,22 @@
 import com.android.server.IpSecService;
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
 
 import org.junit.Before;
 import org.mockito.ArgumentCaptor;
 
+import java.net.InetAddress;
 import java.util.Collections;
 import java.util.UUID;
 
 public class VcnGatewayConnectionTestBase {
     protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
+    protected static final InetAddress TEST_DNS_ADDR =
+            InetAddresses.parseNumericAddress("2001:DB8:0:1::");
+    protected static final LinkAddress TEST_INTERNAL_ADDR =
+            new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:0:2::"), 64);
+
     protected static final int TEST_IPSEC_SPI_VALUE = 0x1234;
     protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1;
     protected static final int TEST_IPSEC_TRANSFORM_RESOURCE_ID = 2;
@@ -86,6 +96,7 @@
     @NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
 
     @NonNull protected final IpSecService mIpSecSvc;
+    @NonNull protected final ConnectivityManager mConnMgr;
 
     protected VcnIkeSession mMockIkeSession;
     protected VcnGatewayConnection mGatewayConnection;
@@ -103,6 +114,10 @@
         mIpSecSvc = mock(IpSecService.class);
         setupIpSecManager(mContext, mIpSecSvc);
 
+        mConnMgr = mock(ConnectivityManager.class);
+        VcnTestUtils.setupSystemService(
+                mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
         doReturn(mContext).when(mVcnContext).getContext();
         doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
         doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
@@ -145,10 +160,10 @@
         return captor.getValue();
     }
 
-    protected ChildSessionCallback getChildSessionCallback() {
+    protected VcnChildSessionCallback getChildSessionCallback() {
         ArgumentCaptor<ChildSessionCallback> captor =
                 ArgumentCaptor.forClass(ChildSessionCallback.class);
         verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture());
-        return captor.getValue();
+        return (VcnChildSessionCallback) captor.getValue();
     }
 }