Merge "Add Universal Game Mode Hint mode switching to GameManagerService."
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 64ba6d1..7bd5921 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -223,6 +223,27 @@
         }
     }
 
+    /** Tests creating and starting a new user. */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void createAndStartUser_realistic() throws RemoteException {
+        while (mRunner.keepRunning()) {
+            Log.d(TAG, "Starting timer");
+            final int userId = createUserNoFlags();
+
+            // Don't use this.startUserInBackgroundAndWaitForUnlock() since only waiting until
+            // ACTION_USER_STARTED.
+            runThenWaitForBroadcasts(userId, () -> {
+                mIam.startUserInBackground(userId);
+            }, Intent.ACTION_USER_STARTED);
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            removeUser(userId);
+            waitCoolDownPeriod();
+            mRunner.resumeTimingForNextIteration();
+        }
+    }
+
     /**
      * Tests starting an uninitialized user.
      * Measures the time until ACTION_USER_STARTED is received.
@@ -299,6 +320,29 @@
         }
     }
 
+    /**
+     * Tests starting & unlocking an uninitialized user.
+     * Measures the time until unlock listener is triggered and user is unlocked.
+     */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void startAndUnlockUser_realistic() throws RemoteException {
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final int userId = createUserNoFlags();
+            mRunner.resumeTiming();
+            Log.d(TAG, "Starting timer");
+
+            // Waits for UserState.mUnlockProgress.finish().
+            startUserInBackgroundAndWaitForUnlock(userId);
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            removeUser(userId);
+            waitCoolDownPeriod();
+            mRunner.resumeTimingForNextIteration();
+        }
+    }
+
     /** Tests switching to an uninitialized user. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void switchUser() throws Exception {
@@ -527,7 +571,7 @@
         removeUser(userId);
     }
 
-    /** Tests reaching LOOKED_BOOT_COMPLETE when switching to uninitialized user. */
+    /** Tests reaching LOCKED_BOOT_COMPLETE when switching to uninitialized user. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void lockedBootCompleted() throws RemoteException {
         while (mRunner.keepRunning()) {
@@ -550,6 +594,29 @@
         }
     }
 
+    /** Tests reaching LOCKED_BOOT_COMPLETE when switching to uninitialized user. */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void lockedBootCompleted_realistic() throws RemoteException {
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final int startUser = ActivityManager.getCurrentUser();
+            final int userId = createUserNoFlags();
+
+            waitCoolDownPeriod();
+            mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
+                mRunner.resumeTiming();
+                Log.d(TAG, "Starting timer");
+                mAm.switchUser(userId);
+            }, () -> fail("Failed to achieve onLockedBootComplete for user " + userId));
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            switchUserNoCheck(startUser);
+            removeUser(userId);
+            mRunner.resumeTimingForNextIteration();
+        }
+    }
+
     /** Tests stopping an ephemeral foreground user. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void ephemeralUserStopped() throws RemoteException {
@@ -579,6 +646,33 @@
         }
     }
 
+    /** Tests stopping an ephemeral foreground user. */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void ephemeralUserStopped_realistic() throws RemoteException {
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final int startUser = ActivityManager.getCurrentUser();
+            final int userId = createUserWithFlags(UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO);
+            runThenWaitForBroadcasts(userId, () -> {
+                switchUser(userId);
+            }, Intent.ACTION_MEDIA_MOUNTED);
+
+            waitCoolDownPeriod();
+            mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
+                runThenWaitForBroadcasts(userId, () -> {
+                    mRunner.resumeTiming();
+                    Log.d(TAG, "Starting timer");
+
+                    mAm.switchUser(startUser);
+                }, Intent.ACTION_USER_STOPPED);
+
+                mRunner.pauseTiming();
+                Log.d(TAG, "Stopping timer");
+            }, null);
+            mRunner.resumeTimingForNextIteration();
+        }
+    }
+
     /** Tests creating a new profile. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileCreate() throws RemoteException {
@@ -596,6 +690,24 @@
         }
     }
 
+    /** Tests creating a new profile. */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void managedProfileCreate_realistic() throws RemoteException {
+        assumeTrue(mHasManagedUserFeature);
+
+        while (mRunner.keepRunning()) {
+            Log.d(TAG, "Starting timer");
+            final int userId = createManagedProfile();
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
+            removeUser(userId);
+            waitCoolDownPeriod();
+            mRunner.resumeTimingForNextIteration();
+        }
+    }
+
     /** Tests starting (unlocking) an uninitialized profile. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileUnlock() throws RemoteException {
@@ -616,6 +728,27 @@
         }
     }
 
+    /** Tests starting (unlocking) an uninitialized profile. */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void managedProfileUnlock_realistic() throws RemoteException {
+        assumeTrue(mHasManagedUserFeature);
+
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final int userId = createManagedProfile();
+            mRunner.resumeTiming();
+            Log.d(TAG, "Starting timer");
+
+            startUserInBackgroundAndWaitForUnlock(userId);
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            removeUser(userId);
+            waitCoolDownPeriod();
+            mRunner.resumeTimingForNextIteration();
+        }
+    }
+
     /** Tests starting (unlocking) a previously-started, but no-longer-running, profile. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileUnlock_stopped() throws RemoteException {
@@ -639,6 +772,29 @@
         }
     }
 
+    /** Tests starting (unlocking) a previously-started, but no-longer-running, profile. */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void managedProfileUnlock_stopped_realistic() throws RemoteException {
+        assumeTrue(mHasManagedUserFeature);
+        final int userId = createManagedProfile();
+        // Start the profile initially, then stop it. Similar to setQuietModeEnabled.
+        startUserInBackgroundAndWaitForUnlock(userId);
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            stopUserAfterWaitingForBroadcastIdle(userId, true);
+            mRunner.resumeTiming();
+            Log.d(TAG, "Starting timer");
+
+            startUserInBackgroundAndWaitForUnlock(userId);
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            waitCoolDownPeriod();
+            mRunner.resumeTimingForNextIteration();
+        }
+        removeUser(userId);
+    }
+
     /**
      * Tests starting (unlocking) & launching an already-installed app in an uninitialized profile.
      */
@@ -665,6 +821,32 @@
     }
 
     /**
+     * Tests starting (unlocking) & launching an already-installed app in an uninitialized profile.
+     */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void managedProfileUnlockAndLaunchApp_realistic() throws RemoteException {
+        assumeTrue(mHasManagedUserFeature);
+
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final int userId = createManagedProfile();
+            WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
+            installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
+            mRunner.resumeTiming();
+            Log.d(TAG, "Starting timer");
+
+            startUserInBackgroundAndWaitForUnlock(userId);
+            startApp(userId, DUMMY_PACKAGE_NAME);
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            removeUser(userId);
+            waitCoolDownPeriod();
+            mRunner.resumeTimingForNextIteration();
+        }
+    }
+
+    /**
      * Tests starting (unlocking) and launching a previously-launched app
      * in a previously-started, but no-longer-running, profile.
      * A sort of combination of {@link #managedProfileUnlockAndLaunchApp} and
@@ -696,6 +878,39 @@
         }
     }
 
+    /**
+     * Tests starting (unlocking) and launching a previously-launched app
+     * in a previously-started, but no-longer-running, profile.
+     * A sort of combination of {@link #managedProfileUnlockAndLaunchApp} and
+     * {@link #managedProfileUnlock_stopped}}.
+     */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void managedProfileUnlockAndLaunchApp_stopped_realistic() throws RemoteException {
+        assumeTrue(mHasManagedUserFeature);
+
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final int userId = createManagedProfile();
+            WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
+            installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
+            startUserInBackgroundAndWaitForUnlock(userId);
+            startApp(userId, DUMMY_PACKAGE_NAME);
+            stopUserAfterWaitingForBroadcastIdle(userId, true);
+            SystemClock.sleep(1_000); // 1 second cool-down before re-starting profile.
+            mRunner.resumeTiming();
+            Log.d(TAG, "Starting timer");
+
+            startUserInBackgroundAndWaitForUnlock(userId);
+            startApp(userId, DUMMY_PACKAGE_NAME);
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            removeUser(userId);
+            waitCoolDownPeriod();
+            mRunner.resumeTimingForNextIteration();
+        }
+    }
+
     /** Tests installing a pre-existing app in an uninitialized profile. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileInstall() throws RemoteException {
@@ -716,6 +931,27 @@
         }
     }
 
+    /** Tests installing a pre-existing app in an uninitialized profile. */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void managedProfileInstall_realistic() throws RemoteException {
+        assumeTrue(mHasManagedUserFeature);
+
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final int userId = createManagedProfile();
+            mRunner.resumeTiming();
+            Log.d(TAG, "Starting timer");
+
+            installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            removeUser(userId);
+            waitCoolDownPeriod();
+            mRunner.resumeTimingForNextIteration();
+        }
+    }
+
     /**
      * Tests creating a new profile, starting (unlocking) it, installing an app,
      * and launching that app in it.
@@ -742,6 +978,33 @@
         }
     }
 
+    /**
+     * Tests creating a new profile, starting (unlocking) it, installing an app,
+     * and launching that app in it.
+     */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void managedProfileCreateUnlockInstallAndLaunchApp_realistic() throws RemoteException {
+        assumeTrue(mHasManagedUserFeature);
+
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
+            mRunner.resumeTiming();
+            Log.d(TAG, "Starting timer");
+
+            final int userId = createManagedProfile();
+            startUserInBackgroundAndWaitForUnlock(userId);
+            installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
+            startApp(userId, DUMMY_PACKAGE_NAME);
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            removeUser(userId);
+            waitCoolDownPeriod();
+            mRunner.resumeTimingForNextIteration();
+        }
+    }
+
     /** Tests stopping a profile. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void managedProfileStopped() throws RemoteException {
@@ -766,6 +1029,30 @@
         }
     }
 
+    /** Tests stopping a profile. */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void managedProfileStopped_realistic() throws RemoteException {
+        assumeTrue(mHasManagedUserFeature);
+        final int userId = createManagedProfile();
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+
+            runThenWaitForBroadcasts(userId, () -> {
+                startUserInBackgroundAndWaitForUnlock(userId);
+            }, Intent.ACTION_MEDIA_MOUNTED);
+            waitCoolDownPeriod();
+            mRunner.resumeTiming();
+            Log.d(TAG, "Starting timer");
+
+            stopUser(userId, true);
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            mRunner.resumeTimingForNextIteration();
+        }
+        removeUser(userId);
+    }
+
     // TODO: This is just a POC. Do this properly and add more.
     /** Tests starting (unlocking) a newly-created profile using the user-type-pkg-whitelist. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index fab5b5f..ad406a1 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -158,17 +158,11 @@
     boolean mForceAllAppStandbyForSmallBattery;
 
     /**
-     * True if the forced app standby feature is enabled in settings
-     */
-    @GuardedBy("mLock")
-    boolean mForcedAppStandbyEnabled;
-
-    /**
      * A lock-free set of (uid, packageName) pairs in background restricted mode.
      *
      * <p>
-     * It's bascially shadowing the {@link #mRunAnyRestrictedPackages} together with
-     * the {@link #mForcedAppStandbyEnabled} - mutations on them would result in copy-on-write.
+     * It's basically shadowing the {@link #mRunAnyRestrictedPackages}, any mutations on it would
+     * result in copy-on-write.
      * </p>
      */
     volatile Set<Pair<Integer, String>> mBackgroundRestrictedUidPackages = Collections.emptySet();
@@ -200,10 +194,9 @@
         int TEMP_EXEMPTION_LIST_CHANGED = 5;
         int EXEMPTED_BUCKET_CHANGED = 6;
         int FORCE_ALL_CHANGED = 7;
-        int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
 
-        int IS_UID_ACTIVE_CACHED = 9;
-        int IS_UID_ACTIVE_RAW = 10;
+        int IS_UID_ACTIVE_CACHED = 8;
+        int IS_UID_ACTIVE_RAW = 9;
     }
 
     private final StatLogger mStatLogger = new StatLogger(new String[] {
@@ -215,7 +208,6 @@
             "TEMP_EXEMPTION_LIST_CHANGED",
             "EXEMPTED_BUCKET_CHANGED",
             "FORCE_ALL_CHANGED",
-            "FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED",
 
             "IS_UID_ACTIVE_CACHED",
             "IS_UID_ACTIVE_RAW",
@@ -228,18 +220,10 @@
         }
 
         void register() {
-            mContext.getContentResolver().registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED),
-                    false, this);
-
             mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED), false, this);
         }
 
-        boolean isForcedAppStandbyEnabled() {
-            return injectGetGlobalSettingInt(Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1;
-        }
-
         boolean isForcedAppStandbyForSmallBatteryEnabled() {
             return injectGetGlobalSettingInt(
                     Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1;
@@ -247,21 +231,7 @@
 
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            if (Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED).equals(uri)) {
-                final boolean enabled = isForcedAppStandbyEnabled();
-                synchronized (mLock) {
-                    if (mForcedAppStandbyEnabled == enabled) {
-                        return;
-                    }
-                    mForcedAppStandbyEnabled = enabled;
-                    updateBackgroundRestrictedUidPackagesLocked();
-                    if (DEBUG) {
-                        Slog.d(TAG, "Forced app standby feature flag changed: "
-                                + mForcedAppStandbyEnabled);
-                    }
-                }
-                mHandler.notifyForcedAppStandbyFeatureFlagChanged();
-            } else if (Settings.Global.getUriFor(
+            if (Settings.Global.getUriFor(
                     Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED).equals(uri)) {
                 final boolean enabled = isForcedAppStandbyForSmallBatteryEnabled();
                 synchronized (mLock) {
@@ -515,7 +485,6 @@
 
             mFlagsObserver = new FeatureFlagsObserver();
             mFlagsObserver.register();
-            mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled();
             mForceAllAppStandbyForSmallBattery =
                     mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled();
             mStandbyTracker = new StandbyTracker();
@@ -636,14 +605,10 @@
 
     /**
      * Update the {@link #mBackgroundRestrictedUidPackages} upon mutations on
-     * {@link #mRunAnyRestrictedPackages} or {@link #mForcedAppStandbyEnabled}.
+     * {@link #mRunAnyRestrictedPackages}.
      */
     @GuardedBy("mLock")
     private void updateBackgroundRestrictedUidPackagesLocked() {
-        if (!mForcedAppStandbyEnabled) {
-            mBackgroundRestrictedUidPackages = Collections.emptySet();
-            return;
-        }
         Set<Pair<Integer, String>> fasUidPkgs = new ArraySet<>();
         for (int i = 0, size = mRunAnyRestrictedPackages.size(); i < size; i++) {
             fasUidPkgs.add(mRunAnyRestrictedPackages.valueAt(i));
@@ -821,13 +786,14 @@
 
     private class MyHandler extends Handler {
         private static final int MSG_UID_ACTIVE_STATE_CHANGED = 0;
+        // Unused ids 1, 2.
         private static final int MSG_RUN_ANY_CHANGED = 3;
         private static final int MSG_ALL_UNEXEMPTED = 4;
         private static final int MSG_ALL_EXEMPTION_LIST_CHANGED = 5;
         private static final int MSG_TEMP_EXEMPTION_LIST_CHANGED = 6;
         private static final int MSG_FORCE_ALL_CHANGED = 7;
         private static final int MSG_USER_REMOVED = 8;
-        private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 9;
+        // Unused id 9.
         private static final int MSG_EXEMPTED_BUCKET_CHANGED = 10;
         private static final int MSG_AUTO_RESTRICTED_BUCKET_FEATURE_FLAG_CHANGED = 11;
 
@@ -867,11 +833,6 @@
             obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
         }
 
-        public void notifyForcedAppStandbyFeatureFlagChanged() {
-            removeMessages(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED);
-            obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
-        }
-
         public void notifyExemptedBucketChanged() {
             removeMessages(MSG_EXEMPTED_BUCKET_CHANGED);
             obtainMessage(MSG_EXEMPTED_BUCKET_CHANGED).sendToTarget();
@@ -966,22 +927,6 @@
                     mStatLogger.logDurationStat(Stats.FORCE_ALL_CHANGED, start);
                     return;
 
-                case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED:
-                    // Feature flag for forced app standby changed.
-                    final boolean unblockAlarms;
-                    synchronized (mLock) {
-                        unblockAlarms = !mForcedAppStandbyEnabled;
-                    }
-                    for (Listener l : cloneListeners()) {
-                        l.updateAllJobs();
-                        if (unblockAlarms) {
-                            l.unblockAllUnrestrictedAlarms();
-                        }
-                    }
-                    mStatLogger.logDurationStat(
-                            Stats.FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED, start);
-                    return;
-
                 case MSG_USER_REMOVED:
                     handleUserRemoved(msg.arg1);
                     return;
@@ -1164,8 +1109,7 @@
             // If apps will be put into restricted standby bucket automatically on user-forced
             // app standby, instead of blocking alarms completely, let the restricted standby bucket
             // policy take care of it.
-            return (mForcedAppStandbyEnabled
-                    && !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
+            return (!mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
                     && isRunAnyRestrictedLocked(uid, packageName));
         }
     }
@@ -1210,8 +1154,7 @@
             // If apps will be put into restricted standby bucket automatically on user-forced
             // app standby, instead of blocking jobs completely, let the restricted standby bucket
             // policy take care of it.
-            if (mForcedAppStandbyEnabled
-                    && !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
+            if (!mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
                     && isRunAnyRestrictedLocked(uid, packageName)) {
                 return true;
             }
@@ -1321,8 +1264,6 @@
             pw.println("Current AppStateTracker State:");
 
             pw.increaseIndent();
-            pw.println("Forced App Standby Feature enabled: " + mForcedAppStandbyEnabled);
-
             pw.print("Force all apps standby: ");
             pw.println(isForceAllAppsStandbyEnabled());
 
@@ -1400,8 +1341,6 @@
         synchronized (mLock) {
             final long token = proto.start(fieldId);
 
-            proto.write(AppStateTrackerProto.FORCED_APP_STANDBY_FEATURE_ENABLED,
-                    mForcedAppStandbyEnabled);
             proto.write(AppStateTrackerProto.FORCE_ALL_APPS_STANDBY,
                     isForceAllAppsStandbyEnabled());
             proto.write(AppStateTrackerProto.IS_SMALL_BATTERY_DEVICE, isSmallBatteryDevice());
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index e41eb00..37d190d 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -5379,9 +5379,7 @@
 
         @Override
         public void unblockAllUnrestrictedAlarms() {
-            // Called when:
-            // 1. Power exemption list changes,
-            // 2. User FAS feature is disabled.
+            // Called when the power exemption list changes.
             synchronized (mLock) {
                 sendAllUnrestrictedPendingBackgroundAlarmsLocked();
             }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index fb5d63e..c2168d2 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -361,7 +361,14 @@
             boolean binding = false;
             try {
                 final int bindFlags;
-                if (job.shouldTreatAsExpeditedJob()) {
+                if (job.shouldTreatAsUserInitiatedJob()) {
+                    // TODO (191785864, 261999509): add an appropriate flag so user-initiated jobs
+                    //    can bypass data saver
+                    bindFlags = Context.BIND_AUTO_CREATE
+                            | Context.BIND_ALMOST_PERCEPTIBLE
+                            | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
+                            | Context.BIND_NOT_APP_COMPONENT_USAGE;
+                } else if (job.shouldTreatAsExpeditedJob()) {
                     bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
                             | Context.BIND_ALMOST_PERCEPTIBLE
                             | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 16f5c7f..b87dec1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -98,6 +98,13 @@
             ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
                     | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
                     | ConnectivityManager.BLOCKED_REASON_DOZE);
+    // TODO(261999509): allow bypassing data saver & user-restricted. However, when we allow a UI
+    //     job to run while data saver restricts the app, we must ensure that we don't run regular
+    //     jobs when we put a hole in the data saver wall for the UI job
+    private static final int UNBYPASSABLE_UI_BLOCKED_REASONS =
+            ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
+                    | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
+                    | ConnectivityManager.BLOCKED_REASON_DOZE);
     private static final int UNBYPASSABLE_FOREGROUND_BLOCKED_REASONS =
             ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
                     | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
@@ -1080,6 +1087,11 @@
                 Slog.d(TAG, "Using FG bypass for " + jobStatus.getSourceUid());
             }
             unbypassableBlockedReasons = UNBYPASSABLE_FOREGROUND_BLOCKED_REASONS;
+        } else if (jobStatus.shouldTreatAsUserInitiatedJob()) {
+            if (DEBUG) {
+                Slog.d(TAG, "Using UI bypass for " + jobStatus.getSourceUid());
+            }
+            unbypassableBlockedReasons = UNBYPASSABLE_UI_BLOCKED_REASONS;
         } else if (jobStatus.shouldTreatAsExpeditedJob() || jobStatus.startedAsExpeditedJob) {
             if (DEBUG) {
                 Slog.d(TAG, "Using EJ bypass for " + jobStatus.getSourceUid());
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 13a2a94..258680a 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -965,17 +965,21 @@
             Slog.d(TAG, "   Checking idle state for " + packageName
                     + " minBucket=" + standbyBucketToString(minBucket));
         }
+        final boolean previouslyIdle, stillIdle;
         if (minBucket <= STANDBY_BUCKET_ACTIVE) {
             // No extra processing needed for ACTIVE or higher since apps can't drop into lower
             // buckets.
             synchronized (mAppIdleLock) {
+                previouslyIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
                 mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
                         minBucket, REASON_MAIN_DEFAULT);
+                stillIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
             }
             maybeInformListeners(packageName, userId, elapsedRealtime,
                     minBucket, REASON_MAIN_DEFAULT, false);
         } else {
             synchronized (mAppIdleLock) {
+                previouslyIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
                 final AppIdleHistory.AppUsageHistory app =
                         mAppIdleHistory.getAppUsageHistory(packageName,
                         userId, elapsedRealtime);
@@ -1073,11 +1077,17 @@
                 if (oldBucket != newBucket || predictionLate) {
                     mAppIdleHistory.setAppStandbyBucket(packageName, userId,
                             elapsedRealtime, newBucket, reason);
+                    stillIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
                     maybeInformListeners(packageName, userId, elapsedRealtime,
                             newBucket, reason, false);
+                } else {
+                    stillIdle = previouslyIdle;
                 }
             }
         }
+        if (previouslyIdle != stillIdle) {
+            notifyBatteryStats(packageName, userId, stillIdle);
+        }
     }
 
     /** Returns true if there hasn't been a prediction for the app in a while. */
@@ -1234,8 +1244,9 @@
                     appHistory.currentBucket, reason, userStartedInteracting);
         }
 
-        if (previouslyIdle) {
-            notifyBatteryStats(pkg, userId, false);
+        final boolean stillIdle = appHistory.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
+        if (previouslyIdle != stillIdle) {
+            notifyBatteryStats(pkg, userId, stillIdle);
         }
     }
 
@@ -1808,8 +1819,14 @@
                 reason = REASON_MAIN_FORCED_BY_SYSTEM
                         | (app.bucketingReason & REASON_SUB_MASK)
                         | (reason & REASON_SUB_MASK);
+                final boolean previouslyIdle =
+                        app.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
                 mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
                         newBucket, reason, resetTimeout);
+                final boolean stillIdle = newBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
+                if (previouslyIdle != stillIdle) {
+                    notifyBatteryStats(packageName, userId, stillIdle);
+                }
                 return;
             }
 
@@ -1910,8 +1927,13 @@
 
             // Make sure we don't put the app in a lower bucket than it's supposed to be in.
             newBucket = Math.min(newBucket, getAppMinBucket(packageName, userId));
+            final boolean previouslyIdle = app.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
             mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
                     reason, resetTimeout);
+            final boolean stillIdle = newBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
+            if (previouslyIdle != stillIdle) {
+                notifyBatteryStats(packageName, userId, stillIdle);
+            }
         }
         maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false);
     }
diff --git a/core/api/current.txt b/core/api/current.txt
index c811a09..10578db 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1488,6 +1488,7 @@
     field public static final int strokeLineJoin = 16843788; // 0x101040c
     field public static final int strokeMiterLimit = 16843789; // 0x101040d
     field public static final int strokeWidth = 16843783; // 0x1010407
+    field public static final int stylusHandwritingSettingsActivity;
     field public static final int subMenuArrow = 16844019; // 0x10104f3
     field public static final int submitBackground = 16843912; // 0x1010488
     field public static final int subtitle = 16843473; // 0x10102d1
@@ -12880,16 +12881,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR;
   }
 
-  public final class UserProperties implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getShowInLauncher();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserProperties> CREATOR;
-    field public static final int SHOW_IN_LAUNCHER_NO = 2; // 0x2
-    field public static final int SHOW_IN_LAUNCHER_SEPARATE = 1; // 0x1
-    field public static final int SHOW_IN_LAUNCHER_WITH_PARENT = 0; // 0x0
-  }
-
   public final class VersionedPackage implements android.os.Parcelable {
     ctor public VersionedPackage(@NonNull String, int);
     ctor public VersionedPackage(@NonNull String, long);
@@ -19044,15 +19035,15 @@
     method public int getSurfaceGroupId();
     method @NonNull public java.util.List<android.view.Surface> getSurfaces();
     method public int getTimestampBase();
-    method public boolean isReadoutTimestampUsed();
+    method public boolean isReadoutTimestampEnabled();
     method public void removeSensorPixelModeUsed(int);
     method public void removeSurface(@NonNull android.view.Surface);
     method public void setDynamicRangeProfile(long);
     method public void setMirrorMode(int);
     method public void setPhysicalCameraId(@Nullable String);
+    method public void setReadoutTimestampEnabled(boolean);
     method public void setStreamUseCase(long);
     method public void setTimestampBase(int);
-    method public void useReadoutTimestamp(boolean);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
     field public static final int MIRROR_MODE_AUTO = 0; // 0x0
@@ -33224,7 +33215,6 @@
     method public android.os.UserHandle getUserForSerialNumber(long);
     method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS", "android.permission.QUERY_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public String getUserName();
     method public java.util.List<android.os.UserHandle> getUserProfiles();
-    method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.QUERY_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.content.pm.UserProperties getUserProperties(@NonNull android.os.UserHandle);
     method public android.os.Bundle getUserRestrictions();
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
     method public boolean hasUserRestriction(String);
@@ -35375,7 +35365,7 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Email implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+    method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
     method public static int getTypeLabelResource(int);
     field public static final String ADDRESS = "data1";
     field public static final android.net.Uri CONTENT_FILTER_URI;
@@ -35396,7 +35386,7 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Event implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+    method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
     method public static int getTypeResource(Integer);
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_event";
     field public static final String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
@@ -35429,7 +35419,7 @@
   public static final class ContactsContract.CommonDataKinds.Im implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
     method public static CharSequence getProtocolLabel(android.content.res.Resources, int, CharSequence);
     method public static int getProtocolLabelResource(int);
-    method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+    method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
     method public static int getTypeLabelResource(int);
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im";
     field public static final String CUSTOM_PROTOCOL = "data6";
@@ -35475,7 +35465,7 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Organization implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+    method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
     method public static int getTypeLabelResource(int);
     field public static final String COMPANY = "data1";
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/organization";
@@ -35494,7 +35484,7 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Phone implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+    method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
     method public static int getTypeLabelResource(int);
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone_v2";
@@ -35541,7 +35531,7 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Relation implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+    method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
     method public static int getTypeLabelResource(int);
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/relation";
     field public static final String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
@@ -35565,7 +35555,7 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.SipAddress implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+    method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
     method public static int getTypeLabelResource(int);
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sip_address";
     field public static final String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
@@ -35596,7 +35586,7 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.StructuredPostal implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+    method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
     method public static int getTypeLabelResource(int);
     field public static final String CITY = "data7";
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/postal-address_v2";
@@ -44545,6 +44535,7 @@
     field public static final int RESULT_SMS_SEND_RETRY_FAILED = 30; // 0x1e
     field public static final int RESULT_SYSTEM_ERROR = 15; // 0xf
     field public static final int RESULT_UNEXPECTED_EVENT_STOP_SENDING = 28; // 0x1c
+    field public static final int RESULT_USER_NOT_ALLOWED = 33; // 0x21
     field public static final int SMS_RP_CAUSE_CALL_BARRING = 10; // 0xa
     field public static final int SMS_RP_CAUSE_CONGESTION = 42; // 0x2a
     field public static final int SMS_RP_CAUSE_DESTINATION_OUT_OF_ORDER = 27; // 0x1b
@@ -52961,10 +52952,10 @@
     method public void onRestrictedCaptionAreaChanged(android.graphics.Rect);
   }
 
-  public final class WindowAnimationFrameStats extends android.view.FrameStats implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.view.WindowAnimationFrameStats> CREATOR;
+  @Deprecated public final class WindowAnimationFrameStats extends android.view.FrameStats implements android.os.Parcelable {
+    method @Deprecated public int describeContents();
+    method @Deprecated public void writeToParcel(android.os.Parcel, int);
+    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.view.WindowAnimationFrameStats> CREATOR;
   }
 
   public final class WindowContentFrameStats extends android.view.FrameStats implements android.os.Parcelable {
@@ -54927,6 +54918,7 @@
   public final class InputMethodInfo implements android.os.Parcelable {
     ctor public InputMethodInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     ctor public InputMethodInfo(String, String, CharSequence, String);
+    method @Nullable public android.content.Intent createStylusHandwritingSettingsActivityIntent();
     method public int describeContents();
     method public void dump(android.util.Printer, String);
     method public android.content.ComponentName getComponent();
@@ -54945,6 +54937,7 @@
     method public boolean supportsStylusHandwriting();
     method public boolean suppressesSpellChecker();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final String ACTION_STYLUS_HANDWRITING_SETTINGS = "android.view.inputmethod.action.STYLUS_HANDWRITING_SETTINGS";
     field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InputMethodInfo> CREATOR;
   }
 
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d3775ad..d3a655c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3908,6 +3908,14 @@
     method @NonNull public android.content.pm.SuspendDialogInfo.Builder setTitle(@NonNull String);
   }
 
+  public final class UserProperties implements android.os.Parcelable {
+    method public int describeContents();
+    method public boolean isCredentialShareableWithParent();
+    method public boolean isMediaSharedWithParent();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserProperties> CREATOR;
+  }
+
 }
 
 package android.content.pm.dex {
@@ -8759,6 +8767,7 @@
     field public static final int TYPE_DVBC = 4; // 0x4
     field public static final int TYPE_DVBS = 5; // 0x5
     field public static final int TYPE_DVBT = 6; // 0x6
+    field public static final int TYPE_IPTV = 11; // 0xb
     field public static final int TYPE_ISDBS = 7; // 0x7
     field public static final int TYPE_ISDBS3 = 8; // 0x8
     field public static final int TYPE_ISDBT = 9; // 0x9
@@ -8781,6 +8790,11 @@
     method public int getHierarchy();
     method public long getInnerFec();
     method @NonNull public int[] getInterleaving();
+    method @IntRange(from=0) public int getIptvAverageJitterMillis();
+    method @NonNull public String getIptvContentUrl();
+    method @IntRange(from=0) public long getIptvPacketsLost();
+    method @IntRange(from=0) public long getIptvPacketsReceived();
+    method @IntRange(from=0) public int getIptvWorstJitterMillis();
     method public int getIsdbtMode();
     method public int getIsdbtPartialReceptionFlag();
     method @IntRange(from=0, to=255) @NonNull public int[] getIsdbtSegment();
@@ -8824,6 +8838,11 @@
     field public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL = 26; // 0x1a
     field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 19; // 0x13
     field public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS = 30; // 0x1e
+    field public static final int FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS = 46; // 0x2e
+    field public static final int FRONTEND_STATUS_TYPE_IPTV_CONTENT_URL = 42; // 0x2a
+    field public static final int FRONTEND_STATUS_TYPE_IPTV_PACKETS_LOST = 43; // 0x2b
+    field public static final int FRONTEND_STATUS_TYPE_IPTV_PACKETS_RECEIVED = 44; // 0x2c
+    field public static final int FRONTEND_STATUS_TYPE_IPTV_WORST_JITTER_MS = 45; // 0x2d
     field public static final int FRONTEND_STATUS_TYPE_ISDBT_MODE = 37; // 0x25
     field public static final int FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG = 38; // 0x26
     field public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS = 31; // 0x1f
@@ -8869,6 +8888,60 @@
     field public static final int FRONTEND_STATUS_READINESS_UNSUPPORTED = 4; // 0x4
   }
 
+  public class IptvFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
+    ctor public IptvFrontendSettings(@NonNull byte[], @NonNull byte[], int, int, @NonNull android.media.tv.tuner.frontend.IptvFrontendSettingsFec, int, int, long, @NonNull String);
+    method @NonNull public static android.media.tv.tuner.frontend.IptvFrontendSettings.Builder builder();
+    method @IntRange(from=0) public long getBitrate();
+    method @NonNull public String getContentUrl();
+    method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress();
+    method public int getDstPort();
+    method @Nullable public android.media.tv.tuner.frontend.IptvFrontendSettingsFec getFec();
+    method public int getIgmp();
+    method public int getProtocol();
+    method @NonNull @Size(min=4, max=16) public byte[] getSrcIpAddress();
+    method public int getSrcPort();
+    method public int getType();
+    field public static final int IGMP_UNDEFINED = 0; // 0x0
+    field public static final int IGMP_V1 = 1; // 0x1
+    field public static final int IGMP_V2 = 2; // 0x2
+    field public static final int IGMP_V3 = 4; // 0x4
+    field public static final int PROTOCOL_RTP = 2; // 0x2
+    field public static final int PROTOCOL_UDP = 1; // 0x1
+    field public static final int PROTOCOL_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class IptvFrontendSettings.Builder {
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings build();
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setBitrate(@IntRange(from=0) long);
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setContentUrl(@NonNull String);
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setDstIpAddress(@NonNull byte[]);
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setDstPort(int);
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setFec(@Nullable android.media.tv.tuner.frontend.IptvFrontendSettingsFec);
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setIgmp(int);
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setProtocol(int);
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setSrcIpAddress(@NonNull byte[]);
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setSrcPort(int);
+  }
+
+  public class IptvFrontendSettingsFec {
+    ctor public IptvFrontendSettingsFec(int, int, int);
+    method @NonNull public static android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder builder();
+    method @IntRange(from=0) public int getFecColNum();
+    method @IntRange(from=0) public int getFecRowNum();
+    method public int getFecType();
+    field public static final int FEC_TYPE_COLUMN = 1; // 0x1
+    field public static final int FEC_TYPE_COLUMN_ROW = 4; // 0x4
+    field public static final int FEC_TYPE_ROW = 2; // 0x2
+    field public static final int FEC_TYPE_UNDEFINED = 0; // 0x0
+  }
+
+  public static final class IptvFrontendSettingsFec.Builder {
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec build();
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder setFecColNum(@IntRange(from=0) int);
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder setFecRowNum(@IntRange(from=0) int);
+    method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder setFecType(int);
+  }
+
   public class Isdbs3FrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
     method public int getCodeRateCapability();
     method public int getModulationCapability();
@@ -10415,6 +10488,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public long[] getSerialNumbersOfUsers(boolean);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon();
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public android.content.pm.UserProperties getUserProperties(@NonNull android.os.UserHandle);
     method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public int getUserRestrictionSource(String, android.os.UserHandle);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public int getUserSwitchability();
@@ -10423,11 +10497,11 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isAdminUser();
     method public boolean isCloneProfile();
-    method public boolean isCredentialSharableWithParent();
+    method @Deprecated public boolean isCredentialSharableWithParent();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isGuestUser();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isMainUser();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
-    method public boolean isMediaSharedWithParent();
+    method @Deprecated public boolean isMediaSharedWithParent();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isPrimaryUser();
     method public static boolean isRemoveResultSuccessful(int);
     method public boolean isRestrictedProfile();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 3a6173c..8dc7dc3 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -759,6 +759,7 @@
     method public int getUserId();
     method public void setAutofillOptions(@Nullable android.content.AutofillOptions);
     method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions);
+    method public void updateDeviceId(int);
     field public static final String ATTENTION_SERVICE = "attention";
     field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
     field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
@@ -945,6 +946,13 @@
     field public String userType;
   }
 
+  public final class UserProperties implements android.os.Parcelable {
+    method public int getShowInLauncher();
+    field public static final int SHOW_IN_LAUNCHER_NO = 2; // 0x2
+    field public static final int SHOW_IN_LAUNCHER_SEPARATE = 1; // 0x1
+    field public static final int SHOW_IN_LAUNCHER_WITH_PARENT = 0; // 0x0
+  }
+
 }
 
 package android.content.res {
@@ -974,6 +982,59 @@
 
 }
 
+package android.credentials {
+
+  public final class CredentialDescription implements android.os.Parcelable {
+    ctor public CredentialDescription(@NonNull String, @NonNull String, @NonNull java.util.List<android.service.credentials.CredentialEntry>);
+    method public int describeContents();
+    method @NonNull public java.util.List<android.service.credentials.CredentialEntry> getCredentialEntries();
+    method @NonNull public String getFlattenedRequestString();
+    method @NonNull public String getType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CredentialDescription> CREATOR;
+  }
+
+  public final class CredentialManager {
+    method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.RegisterCredentialDescriptionException>);
+    method public void unRegisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.UnregisterCredentialDescriptionException>);
+  }
+
+  public class RegisterCredentialDescriptionException extends java.lang.Exception {
+    ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable String);
+    ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable String, @Nullable Throwable);
+    ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable Throwable);
+    ctor public RegisterCredentialDescriptionException(@NonNull String);
+    field @NonNull public final String errorType;
+  }
+
+  public final class RegisterCredentialDescriptionRequest implements android.os.Parcelable {
+    ctor public RegisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
+    ctor public RegisterCredentialDescriptionRequest(@NonNull java.util.List<android.credentials.CredentialDescription>);
+    method public int describeContents();
+    method @NonNull public java.util.List<android.credentials.CredentialDescription> getCredentialDescriptions();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.credentials.RegisterCredentialDescriptionRequest> CREATOR;
+    field public static final String FLATTENED_REQUEST_STRING_KEY = "flattened_request_string";
+  }
+
+  public class UnregisterCredentialDescriptionException extends java.lang.Exception {
+    ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable String);
+    ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable String, @Nullable Throwable);
+    ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable Throwable);
+    ctor public UnregisterCredentialDescriptionException(@NonNull String);
+    field @NonNull public final String errorType;
+  }
+
+  public final class UnregisterCredentialDescriptionRequest implements android.os.Parcelable {
+    ctor public UnregisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
+    method public int describeContents();
+    method @NonNull public android.credentials.CredentialDescription getCredentialDescription();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.credentials.UnregisterCredentialDescriptionRequest> CREATOR;
+  }
+
+}
+
 package android.credentials.ui {
 
   public final class CreateCredentialProviderData extends android.credentials.ui.ProviderData implements android.os.Parcelable {
@@ -3367,6 +3428,7 @@
   }
 
   public final class InputMethodInfo implements android.os.Parcelable {
+    ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, boolean, @NonNull String);
     ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, int);
   }
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 89740af..12899f2 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3062,7 +3062,14 @@
 
     @Override
     public boolean isDeviceContext() {
-        return mIsExplicitDeviceId || isAssociatedWithDisplay();
+        if (mIsExplicitDeviceId) {
+            if (mDeviceId == VirtualDeviceManager.DEVICE_ID_DEFAULT) {
+                return true;
+            }
+            VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class);
+            return vdm.isValidVirtualDeviceId(mDeviceId);
+        }
+        return isAssociatedWithDisplay();
     }
 
     @Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7e5523a..9843c8f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11049,6 +11049,7 @@
      * @throws SecurityException     if the caller is not a profile owner on an organization-owned
      *                               managed profile.
      * @throws IllegalStateException if called after the device setup has been completed.
+     * @throws UnsupportedOperationException if the api is not enabled.
      * @see ManagedSubscriptionsPolicy
      */
     public void setManagedSubscriptionsPolicy(@Nullable ManagedSubscriptionsPolicy policy) {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 1bc3091..d585e8f 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -361,8 +361,12 @@
         private final Context mContext;
         private final IVirtualDeviceManager mService;
         private final IVirtualDevice mVirtualDevice;
+        private final Object mActivityListenersLock = new Object();
+        @GuardedBy("mActivityListenersLock")
         private final ArrayMap<ActivityListener, ActivityListenerDelegate> mActivityListeners =
                 new ArrayMap<>();
+        private final Object mIntentInterceptorListenersLock = new Object();
+        @GuardedBy("mIntentInterceptorListenersLock")
         private final ArrayMap<IntentInterceptorCallback,
                      VirtualIntentInterceptorDelegate> mIntentInterceptorListeners =
                 new ArrayMap<>();
@@ -377,9 +381,11 @@
                     public void onTopActivityChanged(int displayId, ComponentName topActivity) {
                         final long token = Binder.clearCallingIdentity();
                         try {
-                            for (int i = 0; i < mActivityListeners.size(); i++) {
-                                mActivityListeners.valueAt(i)
-                                        .onTopActivityChanged(displayId, topActivity);
+                            synchronized (mActivityListenersLock) {
+                                for (int i = 0; i < mActivityListeners.size(); i++) {
+                                    mActivityListeners.valueAt(i)
+                                            .onTopActivityChanged(displayId, topActivity);
+                                }
                             }
                         } finally {
                             Binder.restoreCallingIdentity(token);
@@ -390,8 +396,10 @@
                     public void onDisplayEmpty(int displayId) {
                         final long token = Binder.clearCallingIdentity();
                         try {
-                            for (int i = 0; i < mActivityListeners.size(); i++) {
-                                mActivityListeners.valueAt(i).onDisplayEmpty(displayId);
+                            synchronized (mActivityListenersLock) {
+                                for (int i = 0; i < mActivityListeners.size(); i++) {
+                                    mActivityListeners.valueAt(i).onDisplayEmpty(displayId);
+                                }
                             }
                         } finally {
                             Binder.restoreCallingIdentity(token);
@@ -960,7 +968,11 @@
          */
         public void addActivityListener(
                 @CallbackExecutor @NonNull Executor executor, @NonNull ActivityListener listener) {
-            mActivityListeners.put(listener, new ActivityListenerDelegate(listener, executor));
+            final ActivityListenerDelegate delegate = new ActivityListenerDelegate(
+                    Objects.requireNonNull(listener), Objects.requireNonNull(executor));
+            synchronized (mActivityListenersLock) {
+                mActivityListeners.put(listener, delegate);
+            }
         }
 
         /**
@@ -971,7 +983,9 @@
          * @see #addActivityListener(Executor, ActivityListener)
          */
         public void removeActivityListener(@NonNull ActivityListener listener) {
-            mActivityListeners.remove(listener);
+            synchronized (mActivityListenersLock) {
+                mActivityListeners.remove(Objects.requireNonNull(listener));
+            }
         }
 
         /**
@@ -999,7 +1013,7 @@
          */
         public void removeSoundEffectListener(@NonNull SoundEffectListener soundEffectListener) {
             synchronized (mSoundEffectListenersLock) {
-                mSoundEffectListeners.remove(soundEffectListener);
+                mSoundEffectListeners.remove(Objects.requireNonNull(soundEffectListener));
             }
         }
 
@@ -1029,7 +1043,9 @@
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
-            mIntentInterceptorListeners.put(interceptorCallback, delegate);
+            synchronized (mIntentInterceptorListenersLock) {
+                mIntentInterceptorListeners.put(interceptorCallback, delegate);
+            }
         }
 
         /**
@@ -1040,14 +1056,17 @@
         public void unregisterIntentInterceptor(
                     @NonNull IntentInterceptorCallback interceptorCallback) {
             Objects.requireNonNull(interceptorCallback);
-            final VirtualIntentInterceptorDelegate delegate =
-                    mIntentInterceptorListeners.get(interceptorCallback);
-            try {
-                mVirtualDevice.unregisterIntentInterceptor(delegate);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
+            final VirtualIntentInterceptorDelegate delegate;
+            synchronized (mIntentInterceptorListenersLock) {
+                delegate = mIntentInterceptorListeners.remove(interceptorCallback);
             }
-            mIntentInterceptorListeners.remove(interceptorCallback);
+            if (delegate != null) {
+                try {
+                    mVirtualDevice.unregisterIntentInterceptor(delegate);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
         }
     }
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7310138..5f1502f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -7342,6 +7342,7 @@
      * @see #createDeviceContext(int)
      * @hide
      */
+    @TestApi
     public void updateDeviceId(int deviceId) {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
@@ -7378,10 +7379,12 @@
     /**
      * Indicates whether the value of {@link Context#getDeviceId()} can be relied upon for
      * this instance. It will return {@code true} for Contexts created by
-     * {@link Context#createDeviceContext(int)}, as well as for UI and Display Contexts.
+     * {@link Context#createDeviceContext(int)} which reference a valid device ID, as well as for
+     * UI and Display Contexts.
      * <p>
      * Contexts created with {@link Context#createDeviceContext(int)} will have an explicit
-     * device association, which will never change. UI Contexts and Display Contexts are
+     * device association, which will never change, even if the underlying device is closed or is
+     * removed. UI Contexts and Display Contexts are
      * already associated with a display, so if the device association is not explicitly
      * given, {@link Context#getDeviceId()} will return the ID of the device associated with
      * the associated display. The system can assign an arbitrary device id value for Contexts not
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 51662af..824d15c 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -19,6 +19,8 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Slog;
@@ -36,7 +38,10 @@
 
 /**
  * Class holding the properties of a user that derive mostly from its user type.
+ *
+ * @hide
  */
+@SystemApi
 public final class UserProperties implements Parcelable {
     private static final String LOG_TAG = UserProperties.class.getSimpleName();
 
@@ -52,6 +57,10 @@
             "crossProfileIntentFilterAccessControl";
     private static final String ATTR_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY =
             "crossProfileIntentResolutionStrategy";
+    private static final String ATTR_MEDIA_SHARED_WITH_PARENT =
+            "mediaSharedWithParent";
+    private static final String ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT =
+            "credentialShareableWithParent";
 
     /** Index values of each property (to indicate whether they are present in this object). */
     @IntDef(prefix = "INDEX_", value = {
@@ -62,7 +71,9 @@
             INDEX_USE_PARENTS_CONTACTS,
             INDEX_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA,
             INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL,
-            INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY
+            INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY,
+            INDEX_MEDIA_SHARED_WITH_PARENT,
+            INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT
     })
     @Retention(RetentionPolicy.SOURCE)
     private @interface PropertyIndex {
@@ -75,6 +86,8 @@
     private static final int INDEX_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA = 5;
     private static final int INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL = 6;
     private static final int INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY = 7;
+    private static final int INDEX_MEDIA_SHARED_WITH_PARENT = 8;
+    private static final int INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT = 9;
     /** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
     private long mPropertiesPresent = 0;
 
@@ -95,16 +108,22 @@
      * Suggests that the launcher should show this user's apps in the main tab.
      * That is, either this user is a full user, so its apps should be presented accordingly, or, if
      * this user is a profile, then its apps should be shown alongside its parent's apps.
+     * @hide
      */
+    @TestApi
     public static final int SHOW_IN_LAUNCHER_WITH_PARENT = 0;
     /**
      * Suggests that the launcher should show this user's apps, but separately from the apps of this
      * user's parent.
+     * @hide
      */
+    @TestApi
     public static final int SHOW_IN_LAUNCHER_SEPARATE = 1;
     /**
      * Suggests that the launcher should not show this user.
+     * @hide
      */
+    @TestApi
     public static final int SHOW_IN_LAUNCHER_NO = 2;
 
     /**
@@ -304,6 +323,8 @@
         }
         // Add items that have no permission requirements at all.
         setShowInLauncher(orig.getShowInLauncher());
+        setMediaSharedWithParent(orig.isMediaSharedWithParent());
+        setCredentialShareableWithParent(orig.isCredentialShareableWithParent());
     }
 
     /**
@@ -337,7 +358,9 @@
      *    and {@link #SHOW_IN_LAUNCHER_NO}.
      *
      * @return whether, and how, a profile should be shown in the Launcher.
+     * @hide
      */
+    @TestApi
     public @ShowInLauncher int getShowInLauncher() {
         if (isPresent(INDEX_SHOW_IN_LAUNCHER)) return mShowInLauncher;
         if (mDefaultProperties != null) return mDefaultProperties.mShowInLauncher;
@@ -463,13 +486,47 @@
         throw new SecurityException("You don't have permission to query "
                 + "updateCrossProfileIntentFiltersOnOTA");
     }
-
     /** @hide */
     public void setUpdateCrossProfileIntentFiltersOnOTA(boolean val) {
         this.mUpdateCrossProfileIntentFiltersOnOTA = val;
         setPresent(INDEX_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA);
     }
 
+    /**
+     * Returns whether a profile shares media with its parent user.
+     * This only applies for users that have parents (i.e. for profiles).
+     */
+    public boolean isMediaSharedWithParent() {
+        if (isPresent(INDEX_MEDIA_SHARED_WITH_PARENT)) return mMediaSharedWithParent;
+        if (mDefaultProperties != null) return mDefaultProperties.mMediaSharedWithParent;
+        throw new SecurityException("You don't have permission to query mediaSharedWithParent");
+    }
+    /** @hide */
+    public void setMediaSharedWithParent(boolean val) {
+        this.mMediaSharedWithParent = val;
+        setPresent(INDEX_MEDIA_SHARED_WITH_PARENT);
+    }
+    private boolean mMediaSharedWithParent;
+
+    /**
+     * Returns whether a profile can have shared lockscreen credential with its parent user.
+     * This only applies for users that have parents (i.e. for profiles).
+     */
+    public boolean isCredentialShareableWithParent() {
+        if (isPresent(INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT)) {
+            return mCredentialShareableWithParent;
+        }
+        if (mDefaultProperties != null) return mDefaultProperties.mCredentialShareableWithParent;
+        throw new SecurityException(
+                "You don't have permission to query credentialShareableWithParent");
+    }
+    /** @hide */
+    public void setCredentialShareableWithParent(boolean val) {
+        this.mCredentialShareableWithParent = val;
+        setPresent(INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT);
+    }
+    private boolean mCredentialShareableWithParent;
+
     /*
      Indicate if {@link com.android.server.pm.CrossProfileIntentFilter}s need to be updated during
      OTA update between user-parent
@@ -550,6 +607,8 @@
                 + getCrossProfileIntentFilterAccessControl()
                 + ", mCrossProfileIntentResolutionStrategy="
                 + getCrossProfileIntentResolutionStrategy()
+                + ", mMediaSharedWithParent=" + isMediaSharedWithParent()
+                + ", mCredentialShareableWithParent=" + isCredentialShareableWithParent()
                 + "}";
     }
 
@@ -572,6 +631,9 @@
                 + getCrossProfileIntentFilterAccessControl());
         pw.println(prefix + "    mCrossProfileIntentResolutionStrategy="
                 + getCrossProfileIntentResolutionStrategy());
+        pw.println(prefix + "    mMediaSharedWithParent=" + isMediaSharedWithParent());
+        pw.println(prefix + "    mCredentialShareableWithParent="
+                + isCredentialShareableWithParent());
     }
 
     /**
@@ -629,6 +691,12 @@
                 case ATTR_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY:
                     setCrossProfileIntentResolutionStrategy(parser.getAttributeInt(i));
                     break;
+                case ATTR_MEDIA_SHARED_WITH_PARENT:
+                    setMediaSharedWithParent(parser.getAttributeBoolean(i));
+                    break;
+                case ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT:
+                    setCredentialShareableWithParent(parser.getAttributeBoolean(i));
+                    break;
                 default:
                     Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
             }
@@ -676,6 +744,14 @@
             serializer.attributeInt(null, ATTR_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY,
                     mCrossProfileIntentResolutionStrategy);
         }
+        if (isPresent(INDEX_MEDIA_SHARED_WITH_PARENT)) {
+            serializer.attributeBoolean(null, ATTR_MEDIA_SHARED_WITH_PARENT,
+                    mMediaSharedWithParent);
+        }
+        if (isPresent(INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT)) {
+            serializer.attributeBoolean(null, ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT,
+                    mCredentialShareableWithParent);
+        }
     }
 
     // For use only with an object that has already had any permission-lacking fields stripped out.
@@ -690,6 +766,8 @@
         dest.writeBoolean(mUpdateCrossProfileIntentFiltersOnOTA);
         dest.writeInt(mCrossProfileIntentFilterAccessControl);
         dest.writeInt(mCrossProfileIntentResolutionStrategy);
+        dest.writeBoolean(mMediaSharedWithParent);
+        dest.writeBoolean(mCredentialShareableWithParent);
     }
 
     /**
@@ -708,6 +786,8 @@
         mUpdateCrossProfileIntentFiltersOnOTA = source.readBoolean();
         mCrossProfileIntentFilterAccessControl = source.readInt();
         mCrossProfileIntentResolutionStrategy = source.readInt();
+        mMediaSharedWithParent = source.readBoolean();
+        mCredentialShareableWithParent = source.readBoolean();
     }
 
     @Override
@@ -743,6 +823,8 @@
                 CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_ALL;
         private @CrossProfileIntentResolutionStrategy int mCrossProfileIntentResolutionStrategy =
                 CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT;
+        private boolean mMediaSharedWithParent = false;
+        private boolean mCredentialShareableWithParent = false;
 
         public Builder setShowInLauncher(@ShowInLauncher int showInLauncher) {
             mShowInLauncher = showInLauncher;
@@ -794,6 +876,16 @@
             return this;
         }
 
+        public Builder setMediaSharedWithParent(boolean mediaSharedWithParent) {
+            mMediaSharedWithParent = mediaSharedWithParent;
+            return this;
+        }
+
+        public Builder setCredentialShareableWithParent(boolean credentialShareableWithParent) {
+            mCredentialShareableWithParent = credentialShareableWithParent;
+            return this;
+        }
+
         /** Builds a UserProperties object with *all* values populated. */
         public UserProperties build() {
             return new UserProperties(
@@ -804,7 +896,9 @@
                     mUseParentsContacts,
                     mUpdateCrossProfileIntentFiltersOnOTA,
                     mCrossProfileIntentFilterAccessControl,
-                    mCrossProfileIntentResolutionStrategy);
+                    mCrossProfileIntentResolutionStrategy,
+                    mMediaSharedWithParent,
+                    mCredentialShareableWithParent);
         }
     } // end Builder
 
@@ -816,7 +910,9 @@
             @InheritDevicePolicy int inheritDevicePolicy,
             boolean useParentsContacts, boolean updateCrossProfileIntentFiltersOnOTA,
             @CrossProfileIntentFilterAccessControlLevel int crossProfileIntentFilterAccessControl,
-            @CrossProfileIntentResolutionStrategy int crossProfileIntentResolutionStrategy) {
+            @CrossProfileIntentResolutionStrategy int crossProfileIntentResolutionStrategy,
+            boolean mediaSharedWithParent,
+            boolean credentialShareableWithParent) {
 
         mDefaultProperties = null;
         setShowInLauncher(showInLauncher);
@@ -827,5 +923,7 @@
         setUpdateCrossProfileIntentFiltersOnOTA(updateCrossProfileIntentFiltersOnOTA);
         setCrossProfileIntentFilterAccessControl(crossProfileIntentFilterAccessControl);
         setCrossProfileIntentResolutionStrategy(crossProfileIntentResolutionStrategy);
+        setMediaSharedWithParent(mediaSharedWithParent);
+        setCredentialShareableWithParent(credentialShareableWithParent);
     }
 }
diff --git a/core/java/android/credentials/CredentialDescription.java b/core/java/android/credentials/CredentialDescription.java
index b4310f2..ec6a396 100644
--- a/core/java/android/credentials/CredentialDescription.java
+++ b/core/java/android/credentials/CredentialDescription.java
@@ -17,6 +17,7 @@
 package android.credentials;
 
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.service.credentials.CredentialEntry;
@@ -32,6 +33,7 @@
  * Represents the type and contained data fields of a {@link Credential}.
  * @hide
  */
+@TestApi
 public final class CredentialDescription implements Parcelable {
 
     /**
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index e15cec8..232d063 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -329,6 +330,7 @@
      *
      * @hide
      */
+    @TestApi
     public void registerCredentialDescription(
             @NonNull RegisterCredentialDescriptionRequest request,
             @Nullable CancellationSignal cancellationSignal,
@@ -376,6 +378,7 @@
      *
      * @hide
      */
+    @TestApi
     public void unRegisterCredentialDescription(
             @NonNull UnregisterCredentialDescriptionRequest request,
             @Nullable CancellationSignal cancellationSignal,
diff --git a/core/java/android/credentials/RegisterCredentialDescriptionException.java b/core/java/android/credentials/RegisterCredentialDescriptionException.java
index 3cf5a75..d19bb8e 100644
--- a/core/java/android/credentials/RegisterCredentialDescriptionException.java
+++ b/core/java/android/credentials/RegisterCredentialDescriptionException.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.os.CancellationSignal;
 import android.os.OutcomeReceiver;
 
@@ -32,6 +33,7 @@
  *
  * @hide
  */
+@TestApi
 public class RegisterCredentialDescriptionException extends Exception {
 
     @NonNull public final String errorType;
diff --git a/core/java/android/credentials/RegisterCredentialDescriptionRequest.java b/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
index de31279..f257ac5 100644
--- a/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
+++ b/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
@@ -19,6 +19,7 @@
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -36,6 +37,7 @@
  *
  * @hide
  */
+@TestApi
 public final class RegisterCredentialDescriptionRequest implements Parcelable {
 
     public static final String FLATTENED_REQUEST_STRING_KEY = "flattened_request_string";
diff --git a/core/java/android/credentials/UnregisterCredentialDescriptionException.java b/core/java/android/credentials/UnregisterCredentialDescriptionException.java
index 0c786bd..a16915a 100644
--- a/core/java/android/credentials/UnregisterCredentialDescriptionException.java
+++ b/core/java/android/credentials/UnregisterCredentialDescriptionException.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.os.CancellationSignal;
 import android.os.OutcomeReceiver;
 
@@ -32,6 +33,7 @@
  *
  * @hide
  */
+@TestApi
 public class UnregisterCredentialDescriptionException extends Exception {
 
     @NonNull public final String errorType;
diff --git a/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java b/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
index f3454c1..6cf40e7 100644
--- a/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
+++ b/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
@@ -19,6 +19,7 @@
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -32,6 +33,7 @@
  *
  * @hide
  */
+@TestApi
 public final class UnregisterCredentialDescriptionRequest implements Parcelable {
 
     @NonNull
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index ff6e897..c9fc722 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -1246,7 +1246,7 @@
          * between frames.</p>
          *
          * <p>The timestamps match the timestamps of the output surfaces with readout timestamp
-         * enabled (via {@link OutputConfiguration#useReadoutTimestamp}) if:</p>
+         * enabled (via {@link OutputConfiguration#setReadoutTimestampEnabled}) if:</p>
          * <ul>
          * <li> Timestamp base is {@link OutputConfiguration#TIMESTAMP_BASE_DEFAULT} and the
          * output
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 11b80cc..f20b25f 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -4600,7 +4600,7 @@
      * {@link CameraCaptureSession.CaptureCallback#onCaptureStarted }.</p>
      * <p>In addition, the application can switch an output surface's timestamp from start of
      * exposure to start of readout by calling
-     * {@link android.hardware.camera2.params.OutputConfiguration#useReadoutTimestamp }.</p>
+     * {@link android.hardware.camera2.params.OutputConfiguration#setReadoutTimestampEnabled }.</p>
      * <p>The readout timestamp is beneficial for video recording, because the encoder favors
      * uniform timestamps, and the readout timestamps better reflect the cadence camera sensors
      * output data.</p>
@@ -5688,4 +5688,5 @@
 
 
 
+
 }
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 3d83009..381c87d 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -4198,4 +4198,5 @@
 
 
 
+
 }
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index dad7d3e..635e79c 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -5699,4 +5699,5 @@
 
 
 
+
 }
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 8b7c5ec..857f62d 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -257,7 +257,7 @@
     /**
      * Timestamp is the start of readout in the same time domain as TIMESTAMP_BASE_SENSOR.
      *
-     * <p>NOTE: do not use! Use useReadoutTimestamp instead.</p>
+     * <p>NOTE: do not use! Use setReadoutTimestampEnabled instead.</p>
      *
      * @hide
      */
@@ -574,7 +574,7 @@
         mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
         mTimestampBase = TIMESTAMP_BASE_DEFAULT;
         mMirrorMode = MIRROR_MODE_AUTO;
-        mUseReadoutTimestamp = false;
+        mReadoutTimestampEnabled = false;
         mIsReadoutSensorTimestampBase = false;
     }
 
@@ -676,7 +676,7 @@
         mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
         mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
         mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
-        mUseReadoutTimestamp = false;
+        mReadoutTimestampEnabled = false;
         mIsReadoutSensorTimestampBase = false;
     }
 
@@ -1050,7 +1050,7 @@
 
         if (timestampBase == TIMESTAMP_BASE_READOUT_SENSOR) {
             mTimestampBase = TIMESTAMP_BASE_SENSOR;
-            mUseReadoutTimestamp = true;
+            mReadoutTimestampEnabled = true;
             mIsReadoutSensorTimestampBase = true;
         } else {
             mTimestampBase = timestampBase;
@@ -1131,14 +1131,16 @@
      * @param on The output image timestamp is the start of exposure time if false, and
      *           the start of readout time if true.
      */
-    public void useReadoutTimestamp(boolean on) {
-        mUseReadoutTimestamp = on;
+    public void setReadoutTimestampEnabled(boolean on) {
+        mReadoutTimestampEnabled = on;
     }
 
     /** Whether readout timestamp is used for this OutputConfiguration.
+     *
+     * @see #setReadoutTimestampEnabled
      */
-    public boolean isReadoutTimestampUsed() {
-        return mUseReadoutTimestamp;
+    public boolean isReadoutTimestampEnabled() {
+        return mReadoutTimestampEnabled;
     }
 
     /**
@@ -1172,7 +1174,7 @@
         this.mStreamUseCase = other.mStreamUseCase;
         this.mTimestampBase = other.mTimestampBase;
         this.mMirrorMode = other.mMirrorMode;
-        this.mUseReadoutTimestamp = other.mUseReadoutTimestamp;
+        this.mReadoutTimestampEnabled = other.mReadoutTimestampEnabled;
     }
 
     /**
@@ -1200,7 +1202,7 @@
 
         int timestampBase = source.readInt();
         int mirrorMode = source.readInt();
-        boolean useReadoutTimestamp = source.readInt() == 1;
+        boolean readoutTimestampEnabled = source.readInt() == 1;
 
         mSurfaceGroupId = surfaceSetId;
         mRotation = rotation;
@@ -1229,7 +1231,7 @@
         mStreamUseCase = streamUseCase;
         mTimestampBase = timestampBase;
         mMirrorMode = mirrorMode;
-        mUseReadoutTimestamp = useReadoutTimestamp;
+        mReadoutTimestampEnabled = readoutTimestampEnabled;
     }
 
     /**
@@ -1350,7 +1352,7 @@
         dest.writeLong(mStreamUseCase);
         dest.writeInt(mTimestampBase);
         dest.writeInt(mMirrorMode);
-        dest.writeInt(mUseReadoutTimestamp ? 1 : 0);
+        dest.writeInt(mReadoutTimestampEnabled ? 1 : 0);
     }
 
     /**
@@ -1385,7 +1387,7 @@
                     mStreamUseCase != other.mStreamUseCase ||
                     mTimestampBase != other.mTimestampBase ||
                     mMirrorMode != other.mMirrorMode ||
-                    mUseReadoutTimestamp != other.mUseReadoutTimestamp)
+                    mReadoutTimestampEnabled != other.mReadoutTimestampEnabled)
                 return false;
             if (mSensorPixelModesUsed.size() != other.mSensorPixelModesUsed.size()) {
                 return false;
@@ -1428,7 +1430,7 @@
                     mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
                     mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
                     mDynamicRangeProfile, mColorSpace, mStreamUseCase,
-                    mTimestampBase, mMirrorMode, mUseReadoutTimestamp ? 1 : 0);
+                    mTimestampBase, mMirrorMode, mReadoutTimestampEnabled ? 1 : 0);
         }
 
         return HashCodeHelpers.hashCode(
@@ -1438,7 +1440,7 @@
                 mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
                 mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
                 mDynamicRangeProfile, mColorSpace, mStreamUseCase, mTimestampBase,
-                mMirrorMode, mUseReadoutTimestamp ? 1 : 0);
+                mMirrorMode, mReadoutTimestampEnabled ? 1 : 0);
     }
 
     private static final String TAG = "OutputConfiguration";
@@ -1480,8 +1482,8 @@
     private int mTimestampBase;
     // Mirroring mode
     private int mMirrorMode;
-    // Use readout timestamp
-    private boolean mUseReadoutTimestamp;
+    // readout timestamp
+    private boolean mReadoutTimestampEnabled;
     // Whether the timestamp base is set to READOUT_SENSOR
     private boolean mIsReadoutSensorTimestampBase;
 }
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index dfd9054..f27c34c 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -175,7 +175,7 @@
      * The <code>android:label</code> attribute specifies a human-readable descriptive
      * label to describe the keyboard layout in the user interface, such as "English (US)".
      * The <code>android:keyboardLayout</code> attribute refers to a
-     * <a href="http://source.android.com/tech/input/key-character-map-files.html">
+     * <a href="https://source.android.com/docs/core/interaction/input/key-character-map-files">
      * key character map</a> resource that defines the keyboard layout.
      * The <code>android:keyboardLocale</code> attribute specifies a comma separated list of BCP 47
      * language tags depicting the locales supported by the keyboard layout. This attribute is
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index ada5c55..62d9c69 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1006,12 +1006,15 @@
         // been replaced with an implementation that will suspendAll and
         // send VM_START.
         System.out.println("Waiting for debugger first packet");
+
+        mWaiting = true;
         while (!isDebuggerConnected()) {
             try {
                 Thread.sleep(100);
             } catch (InterruptedException ie) {
             }
         }
+        mWaiting = false;
 
         System.out.println("Debug.suspendAllAndSentVmStart");
         VMDebug.suspendAllAndSendVmStart();
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index d1d3315..797730b 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -116,8 +116,6 @@
     boolean someUserHasSeedAccount(in String accountName, in String accountType);
     boolean someUserHasAccount(in String accountName, in String accountType);
     String getProfileType(int userId);
-    boolean isMediaSharedWithParent(int userId);
-    boolean isCredentialSharableWithParent(int userId);
     boolean isDemoUser(int userId);
     boolean isPreCreated(int userId);
     UserInfo createProfileForUserEvenWhenDisallowedWithThrow(in String name, in String userType, int flags,
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index dca722e..4ce9184 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -123,6 +123,9 @@
     @TestApi
     public static final int MIN_SECONDARY_USER_ID = 10;
 
+    /** @hide */
+    public static final int MAX_SECONDARY_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE;
+
     /**
      * (Arbitrary) user handle cache size.
      * {@link #CACHED_USER_HANDLES} caches user handles in the range of
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 9a25c70..d63d87d 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3248,7 +3248,10 @@
      * @param userHandle the user handle of the user whose information is being requested.
      * @return a UserProperties object for a specific user.
      * @throws IllegalArgumentException if {@code userHandle} doesn't correspond to an existing user
+     *
+     * @hide
      */
+    @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.MANAGE_USERS,
             android.Manifest.permission.QUERY_USERS,
@@ -5165,19 +5168,25 @@
      * Returns false for any other type of user.
      *
      * @return true if the user shares media with its parent user, false otherwise.
+     *
+     * @deprecated use {@link #getUserProperties(UserHandle)} with
+     *            {@link UserProperties#isMediaSharedWithParent()} instead.
      * @hide
      */
     @SystemApi
+    @Deprecated
     @UserHandleAware(
             requiresAnyOfPermissionsIfNotCallerProfileGroup = {
                     Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.QUERY_USERS,
                     Manifest.permission.INTERACT_ACROSS_USERS})
     @SuppressAutoDoc
     public boolean isMediaSharedWithParent() {
         try {
-            return mService.isMediaSharedWithParent(mUserId);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
+            return getUserProperties(UserHandle.of(mUserId)).isMediaSharedWithParent();
+        } catch (IllegalArgumentException e) {
+            // If the user doesn't exist, return false (for historical reasons)
+            return false;
         }
     }
 
@@ -5187,19 +5196,24 @@
      * This API only works for {@link UserManager#isProfile() profiles}
      * and will always return false for any other user type.
      *
+     * @deprecated use {@link #getUserProperties(UserHandle)} with
+     *            {@link UserProperties#isCredentialShareableWithParent()} instead.
      * @hide
      */
     @SystemApi
+    @Deprecated
     @UserHandleAware(
             requiresAnyOfPermissionsIfNotCallerProfileGroup = {
                     Manifest.permission.MANAGE_USERS,
+                    Manifest.permission.QUERY_USERS,
                     Manifest.permission.INTERACT_ACROSS_USERS})
     @SuppressAutoDoc
     public boolean isCredentialSharableWithParent() {
         try {
-            return mService.isCredentialSharableWithParent(mUserId);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
+            return getUserProperties(UserHandle.of(mUserId)).isCredentialShareableWithParent();
+        } catch (IllegalArgumentException e) {
+            // If the user doesn't exist, return false (for historical reasons)
+            return false;
         }
     }
 
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index a72ccad..80dd488 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -18,17 +18,10 @@
 
 import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
-import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
 import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE;
 import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
-import static android.app.AppOpsManager.OP_READ_MEDIA_AUDIO;
 import static android.app.AppOpsManager.OP_READ_MEDIA_IMAGES;
-import static android.app.AppOpsManager.OP_READ_MEDIA_VIDEO;
-import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE;
-import static android.app.AppOpsManager.OP_WRITE_MEDIA_AUDIO;
-import static android.app.AppOpsManager.OP_WRITE_MEDIA_IMAGES;
-import static android.app.AppOpsManager.OP_WRITE_MEDIA_VIDEO;
 import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.UserHandle.PER_USER_RANGE;
@@ -1869,51 +1862,14 @@
     // handle obscure cases like when an app targets Q but was installed on
     // a device that was originally running on P before being upgraded to Q.
 
-    /** {@hide} */
-    public boolean checkPermissionReadAudio(boolean enforce,
-            int pid, int uid, String packageName, @Nullable String featureId) {
-        if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
-                READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) {
-            return false;
-        }
-        return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
-                OP_READ_MEDIA_AUDIO);
-    }
-
-    /** {@hide} */
-    public boolean checkPermissionWriteAudio(boolean enforce,
-            int pid, int uid, String packageName, @Nullable String featureId) {
-        if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
-                WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
-            return false;
-        }
-        return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
-                OP_WRITE_MEDIA_AUDIO);
-    }
-
-    /** {@hide} */
-    public boolean checkPermissionReadVideo(boolean enforce,
-            int pid, int uid, String packageName, @Nullable String featureId) {
-        if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
-                READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) {
-            return false;
-        }
-        return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
-                OP_READ_MEDIA_VIDEO);
-    }
-
-    /** {@hide} */
-    public boolean checkPermissionWriteVideo(boolean enforce,
-            int pid, int uid, String packageName, @Nullable String featureId) {
-        if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
-                WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
-            return false;
-        }
-        return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
-                OP_WRITE_MEDIA_VIDEO);
-    }
-
-    /** {@hide} */
+    /**
+     * @deprecated This method should not be used since it check slegacy permissions,
+     * no longer valid. Clients should check the appropriate permissions directly
+     * instead (e.g. READ_MEDIA_IMAGES).
+     *
+     * {@hide}
+     */
+    @Deprecated
     public boolean checkPermissionReadImages(boolean enforce,
             int pid, int uid, String packageName, @Nullable String featureId) {
         if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
@@ -1924,17 +1880,6 @@
                 OP_READ_MEDIA_IMAGES);
     }
 
-    /** {@hide} */
-    public boolean checkPermissionWriteImages(boolean enforce,
-            int pid, int uid, String packageName, @Nullable String featureId) {
-        if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
-                WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
-            return false;
-        }
-        return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
-                OP_WRITE_MEDIA_IMAGES);
-    }
-
     private boolean checkExternalStoragePermissionAndAppOp(boolean enforce,
             int pid, int uid, String packageName, @Nullable String featureId, String permission,
             int op) {
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 363d035..bfc5afe 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -6422,7 +6422,7 @@
              * for {@link #TYPE_CUSTOM}.
              */
             public static final CharSequence getTypeLabel(Resources res, int type,
-                    CharSequence label) {
+                    @Nullable CharSequence label) {
                 if ((type == TYPE_CUSTOM || type == TYPE_ASSISTANT) && !TextUtils.isEmpty(label)) {
                     return label;
                 } else {
@@ -6634,7 +6634,7 @@
              * for {@link #TYPE_CUSTOM}.
              */
             public static final CharSequence getTypeLabel(Resources res, int type,
-                    CharSequence label) {
+                    @Nullable CharSequence label) {
                 if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
                     return label;
                 } else {
@@ -6842,7 +6842,7 @@
              * for {@link #TYPE_CUSTOM}.
              */
             public static final CharSequence getTypeLabel(Resources res, int type,
-                    CharSequence label) {
+                    @Nullable CharSequence label) {
                 if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
                     return label;
                 } else {
@@ -7003,7 +7003,7 @@
              * for {@link #TYPE_CUSTOM}.
              */
             public static final CharSequence getTypeLabel(Resources res, int type,
-                    CharSequence label) {
+                    @Nullable CharSequence label) {
                 if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
                     return label;
                 } else {
@@ -7210,7 +7210,7 @@
              * for {@link #TYPE_CUSTOM}.
              */
             public static final CharSequence getTypeLabel(Resources res, int type,
-                    CharSequence label) {
+                    @Nullable CharSequence label) {
                 if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
                     return label;
                 } else {
@@ -7337,7 +7337,7 @@
              * for {@link #TYPE_CUSTOM}.
              */
             public static final CharSequence getTypeLabel(Resources res, int type,
-                    CharSequence label) {
+                    @Nullable CharSequence label) {
                 if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
                     return label;
                 } else {
@@ -7433,7 +7433,7 @@
              * for {@link #TYPE_CUSTOM}.
              */
             public static final CharSequence getTypeLabel(Resources res, int type,
-                    CharSequence label) {
+                    @Nullable CharSequence label) {
                 if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
                     return label;
                 } else {
@@ -7762,7 +7762,7 @@
              * for {@link #TYPE_CUSTOM}.
              */
             public static final CharSequence getTypeLabel(Resources res, int type,
-                    CharSequence label) {
+                    @Nullable CharSequence label) {
                 if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
                     return label;
                 } else {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5b05f21..9de424f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9738,7 +9738,7 @@
         /**
          * Indicates whether "seen" notifications should be suppressed from the lockscreen.
          * <p>
-         * Type: int (0 for false, 1 for true)
+         * Type: int (0 for unset, 1 for true, 2 for false)
          *
          * @hide
          */
@@ -14568,15 +14568,6 @@
                 "app_auto_restriction_enabled";
 
         /**
-         * Feature flag to enable or disable the Forced App Standby feature.
-         * Type: int (0 for false, 1 for true)
-         * Default: 1
-         * @hide
-         */
-        @Readable
-        public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";
-
-        /**
          * Whether or not to enable Forced App Standby on small battery devices.
          * Type: int (0 for false, 1 for true)
          * Default: 0
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
index a389223..ff14404 100644
--- a/core/java/android/service/dreams/DreamActivity.java
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -70,7 +70,7 @@
 
     @Override
     public void onDestroy() {
-        if (mCallback != null) {
+        if (mCallback != null && !isFinishing()) {
             mCallback.onActivityDestroyed();
         }
 
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index df78827..99a7fe5 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -640,6 +640,10 @@
             mConstructorArgs[0] = inflaterContext;
             View result = root;
 
+            if (root != null && root.getViewRootImpl() != null) {
+                root.getViewRootImpl().notifyRendererOfExpensiveFrame();
+            }
+
             try {
                 advanceToRootNode(parser);
                 final String name = parser.getName();
@@ -662,6 +666,10 @@
                     // Temp is the root view that was found in the xml
                     final View temp = createViewFromTag(root, name, inflaterContext, attrs);
 
+                    if (root == null && temp != null && temp.getViewRootImpl() != null) {
+                        temp.getViewRootImpl().notifyRendererOfExpensiveFrame();
+                    }
+
                     ViewGroup.LayoutParams params = null;
 
                     if (root != null) {
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 164a494..9c6e823 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -594,6 +594,13 @@
         }
     }
 
+    @Override
+    public void notifyExpensiveFrame() {
+        if (isEnabled()) {
+            super.notifyExpensiveFrame();
+        }
+    }
+
     /**
      * Updates the light position based on the position of the window.
      *
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5dccd06..5165478 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2334,6 +2334,18 @@
         }
     }
 
+    /**
+     * Notifies the HardwareRenderer of an expensive upcoming frame, to
+     * allow better handling of power and scheduling requirements.
+     *
+     * @hide
+     */
+    void notifyRendererOfExpensiveFrame() {
+        if (mAttachInfo.mThreadedRenderer != null) {
+            mAttachInfo.mThreadedRenderer.notifyExpensiveFrame();
+        }
+    }
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     void scheduleTraversals() {
         if (!mTraversalScheduled) {
diff --git a/core/java/android/view/WindowAnimationFrameStats.java b/core/java/android/view/WindowAnimationFrameStats.java
index 251ae9b..25f29a7 100644
--- a/core/java/android/view/WindowAnimationFrameStats.java
+++ b/core/java/android/view/WindowAnimationFrameStats.java
@@ -32,7 +32,12 @@
  * #getRefreshPeriodNano()}. If the system does not render a frame every refresh
  * period the user will see irregular window transitions. The time when the frame was
  * actually presented on the display by calling {@link #getFramePresentedTimeNano(int)}.
+ *
+ * @deprecated Use Shared
+ *             <a href="https://perfetto.dev/docs/data-sources/frametimeline">FrameTimeline</a>
+ *             jank metrics instead.
  */
+@Deprecated
 public final class WindowAnimationFrameStats extends FrameStats implements Parcelable {
     /**
      * @hide
diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java
index 141849f..b74b80e 100644
--- a/core/java/android/view/WindowMetrics.java
+++ b/core/java/android/view/WindowMetrics.java
@@ -20,6 +20,8 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 
+import java.util.function.Supplier;
+
 /**
  * Metrics about a Window, consisting of the bounds and {@link WindowInsets}.
  * <p>
@@ -50,8 +52,9 @@
 public final class WindowMetrics {
     @NonNull
     private final Rect mBounds;
-    @NonNull
-    private final WindowInsets mWindowInsets;
+
+    private WindowInsets mWindowInsets;
+    private Supplier<WindowInsets> mWindowInsetsSupplier;
 
     /** @see android.util.DisplayMetrics#density */
     private final float mDensity;
@@ -81,6 +84,21 @@
     }
 
     /**
+     * Similar to {@link #WindowMetrics(Rect, WindowInsets, float)} but the window insets are
+     * computed when {@link #getWindowInsets()} is first time called. This reduces unnecessary
+     * calculation and the overhead of obtaining insets state from server side because most
+     * callers are usually only interested in {@link #getBounds()}.
+     *
+     * @hide
+     */
+    public WindowMetrics(@NonNull Rect bounds, @NonNull Supplier<WindowInsets> windowInsetsSupplier,
+            float density) {
+        mBounds = bounds;
+        mWindowInsetsSupplier = windowInsetsSupplier;
+        mDensity = density;
+    }
+
+    /**
      * Returns the bounds of the area associated with this window or
      * {@link android.annotation.UiContext}.
      * <p>
@@ -121,7 +139,10 @@
      */
     @NonNull
     public WindowInsets getWindowInsets() {
-        return mWindowInsets;
+        if (mWindowInsets != null) {
+            return mWindowInsets;
+        }
+        return mWindowInsets = mWindowInsetsSupplier.get();
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 229cc02..ec1badb 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -22,6 +22,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -38,6 +39,7 @@
 import android.inputmethodservice.InputMethodService;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Printer;
 import android.util.Slog;
@@ -71,6 +73,16 @@
  * @attr ref android.R.styleable#InputMethod_configChanges
  */
 public final class InputMethodInfo implements Parcelable {
+
+    /**
+     * {@link Intent#getAction() Intent action} for IME that
+     * {@link #supportsStylusHandwriting() supports stylus handwriting}.
+     *
+     * @see #createStylusHandwritingSettingsActivityIntent().
+     */
+    public static final String ACTION_STYLUS_HANDWRITING_SETTINGS =
+            "android.view.inputmethod.action.STYLUS_HANDWRITING_SETTINGS";
+
     static final String TAG = "InputMethodInfo";
 
     /**
@@ -152,6 +164,11 @@
      */
     private final boolean mSupportsStylusHandwriting;
 
+    /**
+     * The stylus handwriting setting activity's name, used by the system settings to
+     * launch the stylus handwriting specific setting activity of this input method.
+     */
+    private final String mStylusHandwritingSettingsActivityAttr;
 
     /**
      * @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
@@ -203,6 +220,7 @@
 
         PackageManager pm = context.getPackageManager();
         String settingsActivityComponent = null;
+        String stylusHandwritingSettingsActivity = null;
         boolean isVrOnly;
         int isDefaultResId = 0;
 
@@ -253,6 +271,8 @@
                     com.android.internal.R.styleable.InputMethod_configChanges, 0);
             mSupportsStylusHandwriting = sa.getBoolean(
                     com.android.internal.R.styleable.InputMethod_supportsStylusHandwriting, false);
+            stylusHandwritingSettingsActivity = sa.getString(
+                    com.android.internal.R.styleable.InputMethod_stylusHandwritingSettingsActivity);
             sa.recycle();
 
             final int depth = parser.getDepth();
@@ -328,6 +348,7 @@
         }
         mSubtypes = new InputMethodSubtypeArray(subtypes);
         mSettingsActivityName = settingsActivityComponent;
+        mStylusHandwritingSettingsActivityAttr = stylusHandwritingSettingsActivity;
         mIsDefaultResId = isDefaultResId;
         mIsAuxIme = isAuxIme;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
@@ -359,6 +380,7 @@
         mHandledConfigChanges = source.mHandledConfigChanges;
         mSupportsStylusHandwriting = source.mSupportsStylusHandwriting;
         mForceDefault = source.mForceDefault;
+        mStylusHandwritingSettingsActivityAttr = source.mStylusHandwritingSettingsActivityAttr;
     }
 
     InputMethodInfo(Parcel source) {
@@ -376,6 +398,7 @@
         mSubtypes = new InputMethodSubtypeArray(source);
         mHandledConfigChanges = source.readInt();
         mSupportsStylusHandwriting = source.readBoolean();
+        mStylusHandwritingSettingsActivityAttr = source.readString8();
         mForceDefault = false;
     }
 
@@ -389,10 +412,28 @@
                 false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
                 false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
                 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */,
+                null /* stylusHandwritingSettingsActivityAttr */,
                 false /* inlineSuggestionsEnabled */);
     }
 
     /**
+     * Test API for creating a built-in input method to verify stylus handwriting.
+     * @hide
+     */
+    @TestApi
+    public InputMethodInfo(@NonNull String packageName, @NonNull String className,
+            @NonNull CharSequence label, @NonNull String settingsActivity,
+            boolean supportStylusHandwriting,
+            @NonNull String stylusHandwritingSettingsActivityAttr) {
+        this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
+                settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
+                false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
+                false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
+                0 /* handledConfigChanges */, supportStylusHandwriting,
+                stylusHandwritingSettingsActivityAttr, false /* inlineSuggestionsEnabled */);
+    }
+
+    /**
      * Temporary API for creating a built-in input method for test.
      * @hide
      */
@@ -405,6 +446,7 @@
                 false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
                 false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges,
                 false /* supportsStylusHandwriting */,
+                null /* stylusHandwritingSettingsActivityAttr */,
                 false /* inlineSuggestionsEnabled */);
     }
 
@@ -419,6 +461,7 @@
                 true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
                 false /* isVrOnly */, 0 /* handledconfigChanges */,
                 false /* supportsStylusHandwriting */,
+                null /* stylusHandwritingSettingsActivityAttr */,
                 false /* inlineSuggestionsEnabled */);
     }
 
@@ -432,6 +475,7 @@
         this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
                 supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly,
                 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */,
+                null /* stylusHandwritingSettingsActivityAttr */,
                 false /* inlineSuggestionsEnabled */);
     }
 
@@ -443,6 +487,7 @@
             List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
             boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
             boolean isVrOnly, int handledConfigChanges, boolean supportsStylusHandwriting,
+            String stylusHandwritingSettingsActivityAttr,
             boolean supportsInlineSuggestionsWithTouchExploration) {
         final ServiceInfo si = ri.serviceInfo;
         mService = ri;
@@ -461,6 +506,7 @@
         mIsVrOnly = isVrOnly;
         mHandledConfigChanges = handledConfigChanges;
         mSupportsStylusHandwriting = supportsStylusHandwriting;
+        mStylusHandwritingSettingsActivityAttr = stylusHandwritingSettingsActivityAttr;
     }
 
     private static ResolveInfo buildFakeResolveInfo(String packageName, String className,
@@ -550,6 +596,7 @@
      *
      * <p>A null will be returned if there is no settings activity associated
      * with the input method.</p>
+     * @see #createStylusHandwritingSettingsActivityIntent()
      */
     public String getSettingsActivity() {
         return mSettingsActivityName;
@@ -622,11 +669,41 @@
     /**
      * Returns if IME supports handwriting using stylus input.
      * @attr ref android.R.styleable#InputMethod_supportsStylusHandwriting
+     * @see #createStylusHandwritingSettingsActivityIntent()
      */
     public boolean supportsStylusHandwriting() {
         return mSupportsStylusHandwriting;
     }
 
+    /**
+     * Returns {@link Intent} for stylus handwriting settings activity with
+     * {@link Intent#getAction() Intent action} {@link #ACTION_STYLUS_HANDWRITING_SETTINGS}
+     * if IME {@link #supportsStylusHandwriting() supports stylus handwriting}, else
+     * <code>null</code> if there are no associated settings for stylus handwriting / handwriting
+     * is not supported or if
+     * {@link android.R.styleable#InputMethod_stylusHandwritingSettingsActivity} is not defined.
+     *
+     * <p>To launch stylus settings, use this method to get the {@link android.content.Intent} to
+     * launch the stylus handwriting settings activity.</p>
+     * <p>e.g.<pre><code>startActivity(createStylusHandwritingSettingsActivityIntent());</code>
+     * </pre></p>
+     *
+     * @attr ref R.styleable#InputMethod_stylusHandwritingSettingsActivity
+     * @see #getSettingsActivity()
+     * @see #supportsStylusHandwriting()
+     */
+    @Nullable
+    public Intent createStylusHandwritingSettingsActivityIntent() {
+        if (TextUtils.isEmpty(mStylusHandwritingSettingsActivityAttr)
+                || !mSupportsStylusHandwriting) {
+            return null;
+        }
+        // TODO(b/210039666): consider returning null if component is not enabled.
+        return new Intent(ACTION_STYLUS_HANDWRITING_SETTINGS).setComponent(
+                new ComponentName(getServiceInfo().packageName,
+                        mStylusHandwritingSettingsActivityAttr));
+    }
+
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "mId=" + mId
                 + " mSettingsActivityName=" + mSettingsActivityName
@@ -637,7 +714,9 @@
                 + mSupportsInlineSuggestionsWithTouchExploration
                 + " mSuppressesSpellChecker=" + mSuppressesSpellChecker
                 + " mShowInInputMethodPicker=" + mShowInInputMethodPicker
-                + " mSupportsStylusHandwriting=" + mSupportsStylusHandwriting);
+                + " mSupportsStylusHandwriting=" + mSupportsStylusHandwriting
+                + " mStylusHandwritingSettingsActivityAttr="
+                        + mStylusHandwritingSettingsActivityAttr);
         pw.println(prefix + "mIsDefaultResId=0x"
                 + Integer.toHexString(mIsDefaultResId));
         pw.println(prefix + "Service:");
@@ -752,6 +831,7 @@
         mSubtypes.writeToParcel(dest);
         dest.writeInt(mHandledConfigChanges);
         dest.writeBoolean(mSupportsStylusHandwriting);
+        dest.writeString8(mStylusHandwritingSettingsActivityAttr);
     }
 
     /**
diff --git a/core/java/android/window/WindowMetricsController.java b/core/java/android/window/WindowMetricsController.java
index 06449d5..11bd47d 100644
--- a/core/java/android/window/WindowMetricsController.java
+++ b/core/java/android/window/WindowMetricsController.java
@@ -41,6 +41,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Supplier;
 
 /**
  * A controller to handle {@link android.view.WindowMetrics} related APIs, which are
@@ -53,6 +54,9 @@
  * @hide
  */
 public final class WindowMetricsController {
+    // TODO(b/151908239): Remove and always enable this if it is stable.
+    private static final boolean LAZY_WINDOW_INSETS = android.os.SystemProperties.getBoolean(
+            "persist.wm.debug.win_metrics_lazy_insets", false);
     private final Context mContext;
 
     public WindowMetricsController(@NonNull Context context) {
@@ -92,16 +96,11 @@
             windowingMode = winConfig.getWindowingMode();
         }
         final IBinder token = Context.getToken(mContext);
-        final WindowInsets windowInsets = getWindowInsetsFromServerForCurrentDisplay(token,
-                bounds, isScreenRound, windowingMode);
-        return new WindowMetrics(bounds, windowInsets, density);
-    }
-
-    private WindowInsets getWindowInsetsFromServerForCurrentDisplay(
-            IBinder token, Rect bounds, boolean isScreenRound,
-            @WindowConfiguration.WindowingMode int windowingMode) {
-        return getWindowInsetsFromServerForDisplay(mContext.getDisplayId(), token, bounds,
-                isScreenRound, windowingMode);
+        final Supplier<WindowInsets> insetsSupplier = () -> getWindowInsetsFromServerForDisplay(
+                mContext.getDisplayId(), token, bounds, isScreenRound, windowingMode);
+        return LAZY_WINDOW_INSETS
+                ? new WindowMetrics(new Rect(bounds), insetsSupplier, density)
+                : new WindowMetrics(new Rect(bounds), insetsSupplier.get(), density);
     }
 
     /**
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index a704eb3..1c85ca2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -53,7 +53,7 @@
             boolean showImeSwitcher);
     void setWindowState(int display, int window, int state);
 
-    void showRecentApps(boolean triggeredFromAltTab, boolean forward);
+    void showRecentApps(boolean triggeredFromAltTab);
     void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
     void toggleRecentApps();
     void toggleSplitScreen();
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 22d741c..826624a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3765,6 +3765,9 @@
             {@link android.inputmethodservice.InputMethodService#onFinishInput()}.
         -->
         <attr name="supportsStylusHandwriting" format="boolean" />
+        <!-- Class name of an activity that allows the user to modify the stylus handwriting
+            settings for this service -->
+        <attr name="stylusHandwritingSettingsActivity" format="string" />
 
     </declare-styleable>
 
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index f4b49e6..58b8601 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -128,6 +128,7 @@
     <public name="isCredential"/>
     <public name="searchResultHighlightColor" />
     <public name="focusedSearchResultHighlightColor" />
+    <public name="stylusHandwritingSettingsActivity" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01cd0000">
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index e7d7d640..7bef55e 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -62,6 +62,8 @@
         assertThat(clone.supportsSwitchingToNextInputMethod(), is(false));
         assertThat(imi.isInlineSuggestionsEnabled(), is(false));
         assertThat(imi.supportsInlineSuggestionsWithTouchExploration(), is(false));
+        assertThat(imi.supportsStylusHandwriting(), is(false));
+        assertThat(imi.createStylusHandwritingSettingsActivityIntent(), equalTo(null));
     }
 
     @Test
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 89d6304..f815a5e 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -992,6 +992,15 @@
     }
 
     /**
+     * Notifies the hardware renderer about upcoming expensive frames.
+     *
+     * @hide
+     */
+    public void notifyExpensiveFrame() {
+        nNotifyExpensiveFrame(mNativeProxy);
+    }
+
+    /**
      * b/68769804, b/66945974: For low FPS experiments.
      *
      * @hide
@@ -1553,4 +1562,6 @@
     private static native void nSetRtAnimationsEnabled(boolean rtAnimationsEnabled);
 
     private static native void nNotifyCallbackPending(long nativeProxy);
+
+    private static native void nNotifyExpensiveFrame(long nativeProxy);
 }
diff --git a/graphics/java/android/graphics/drawable/LottieDrawable.java b/graphics/java/android/graphics/drawable/LottieDrawable.java
deleted file mode 100644
index c1f1b50..0000000
--- a/graphics/java/android/graphics/drawable/LottieDrawable.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.drawable;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-
-import dalvik.annotation.optimization.FastNative;
-
-import libcore.util.NativeAllocationRegistry;
-
-import java.io.IOException;
-
-/**
- * {@link Drawable} for drawing Lottie files.
- *
- * <p>The framework handles decoding subsequent frames in another thread and
- * updating when necessary. The drawable will only animate while it is being
- * displayed.</p>
- *
- * @hide
- */
-@SuppressLint("NotCloseable")
-public class LottieDrawable extends Drawable implements Animatable {
-    private long mNativePtr;
-
-    /**
-     * Create an animation from the provided JSON string
-     * @hide
-     */
-    private LottieDrawable(@NonNull String animationJson) throws IOException {
-        mNativePtr = nCreate(animationJson);
-        if (mNativePtr == 0) {
-            throw new IOException("could not make LottieDrawable from json");
-        }
-
-        final long nativeSize = nNativeByteSize(mNativePtr);
-        NativeAllocationRegistry registry = new NativeAllocationRegistry(
-                LottieDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize);
-        registry.registerNativeAllocation(this, mNativePtr);
-    }
-
-    /**
-     * Factory for LottieDrawable, throws IOException if native Skottie Builder fails to parse
-     */
-    public static LottieDrawable makeLottieDrawable(@NonNull String animationJson)
-            throws IOException {
-        return new LottieDrawable(animationJson);
-    }
-
-
-
-    /**
-     * Draw the current frame to the Canvas.
-     * @hide
-     */
-    @Override
-    public void draw(@NonNull Canvas canvas) {
-        if (mNativePtr == 0) {
-            throw new IllegalStateException("called draw on empty LottieDrawable");
-        }
-
-        nDraw(mNativePtr, canvas.getNativeCanvasWrapper());
-    }
-
-    /**
-     * Start the animation. Needs to be called before draw calls.
-     * @hide
-     */
-    @Override
-    public void start() {
-        if (mNativePtr == 0) {
-            throw new IllegalStateException("called start on empty LottieDrawable");
-        }
-
-        if (nStart(mNativePtr)) {
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Stops the animation playback. Does not release anything.
-     * @hide
-     */
-    @Override
-    public void stop() {
-        if (mNativePtr == 0) {
-            throw new IllegalStateException("called stop on empty LottieDrawable");
-        }
-        nStop(mNativePtr);
-    }
-
-    /**
-     *  Return whether the animation is currently running.
-     */
-    @Override
-    public boolean isRunning() {
-        if (mNativePtr == 0) {
-            throw new IllegalStateException("called isRunning on empty LottieDrawable");
-        }
-        return nIsRunning(mNativePtr);
-    }
-
-    @Override
-    public int getOpacity() {
-        // We assume translucency to avoid checking each pixel.
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        //TODO
-    }
-
-    @Override
-    public void setColorFilter(@Nullable ColorFilter colorFilter) {
-        //TODO
-    }
-
-    private static native long nCreate(String json);
-    private static native void nDraw(long nativeInstance, long nativeCanvas);
-    @FastNative
-    private static native long nGetNativeFinalizer();
-    @FastNative
-    private static native long nNativeByteSize(long nativeInstance);
-    @FastNative
-    private static native boolean nIsRunning(long nativeInstance);
-    @FastNative
-    private static native boolean nStart(long nativeInstance);
-    @FastNative
-    private static native boolean nStop(long nativeInstance);
-
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index b9caf62..26f47fc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -114,7 +114,9 @@
                 t.setPosition(leash, positionInParent.x, positionInParent.y);
                 t.setAlpha(leash, 1f);
                 t.setMatrix(leash, 1, 0, 0, 1);
-                t.show(leash);
+                if (taskInfo.isVisible) {
+                    t.show(leash);
+                }
             });
         }
     }
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 38099fc..21eeaa2 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,7 @@
 
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -535,17 +536,11 @@
                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else {
-                try {
-                    adapter.getRunner().onAnimationCancelled(false /* isKeyguardOccluded */);
-                    ActivityTaskManager.getService().startActivityFromRecents(taskId, options2);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Error starting remote animation", e);
-                }
+                taskId = INVALID_TASK_ID;
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
                         "Cancel entering split as not supporting multi-instances");
                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
                         Toast.LENGTH_SHORT).show();
-                return;
             }
         }
         mStageCoordinator.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
@@ -586,17 +581,11 @@
                 fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else {
-                try {
-                    adapter.getRunner().onAnimationCancelled(false /* isKeyguardOccluded */);
-                    pendingIntent1.send();
-                } catch (RemoteException | PendingIntent.CanceledException e) {
-                    Slog.e(TAG, "Error starting remote animation", e);
-                }
+                pendingIntent2 = null;
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
                         "Cancel entering split as not supporting multi-instances");
                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
                         Toast.LENGTH_SHORT).show();
-                return;
             }
         }
         mStageCoordinator.startIntentsWithLegacyTransition(pendingIntent1, fillInIntent1, options1,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index a6c4ac2..39cf5f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -629,9 +629,19 @@
             RemoteAnimationAdapter adapter, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
+        if (pendingIntent2 == null) {
+            // Launching a solo task.
+            ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+            activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
+            options1 = activityOptions.toBundle();
+            addActivityOptions(options1, null /* launchTarget */);
+            wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
+            mSyncQueue.queue(wct);
+            return;
+        }
+
         addActivityOptions(options1, mSideStage);
         wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
-
         startWithLegacyTransition(wct, pendingIntent2, fillInIntent2, options2, splitPosition,
                 splitRatio, adapter, instanceId);
     }
@@ -666,9 +676,19 @@
             InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
+        if (taskId == INVALID_TASK_ID) {
+            // Launching a solo task.
+            ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+            activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
+            options1 = activityOptions.toBundle();
+            addActivityOptions(options1, null /* launchTarget */);
+            wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
+            mSyncQueue.queue(wct);
+            return;
+        }
+
         addActivityOptions(options1, mSideStage);
         wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
-
         startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
                 instanceId);
     }
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 9112b1b..8d4bda2 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -78,7 +78,6 @@
                 "external/skia/src/utils",
                 "external/skia/src/gpu",
                 "external/skia/src/shaders",
-                "external/skia/modules/skottie",
             ],
         },
         host: {
@@ -388,7 +387,6 @@
         "external/skia/src/effects",
         "external/skia/src/image",
         "external/skia/src/images",
-        "external/skia/modules/skottie",
     ],
 
     shared_libs: [
@@ -420,7 +418,6 @@
                 "jni/BitmapRegionDecoder.cpp",
                 "jni/GIFMovie.cpp",
                 "jni/GraphicsStatsService.cpp",
-                "jni/LottieDrawable.cpp",
                 "jni/Movie.cpp",
                 "jni/MovieImpl.cpp",
                 "jni/pdf/PdfDocument.cpp",
@@ -528,7 +525,6 @@
         "hwui/BlurDrawLooper.cpp",
         "hwui/Canvas.cpp",
         "hwui/ImageDecoder.cpp",
-        "hwui/LottieDrawable.cpp",
         "hwui/MinikinSkia.cpp",
         "hwui/MinikinUtils.cpp",
         "hwui/PaintImpl.cpp",
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 1e0c359..d0124f5 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -740,10 +740,6 @@
     return imgDrawable->drawStaging(mCanvas);
 }
 
-void SkiaCanvas::drawLottie(LottieDrawable* lottieDrawable) {
-    lottieDrawable->drawStaging(mCanvas);
-}
-
 void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) {
     vectorDrawable->drawStaging(this);
 }
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 1524dff..f2c286a 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -144,7 +144,6 @@
                                float dstTop, float dstRight, float dstBottom,
                                const Paint* paint) override;
     virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) override;
-    virtual void drawLottie(LottieDrawable* lottieDrawable) override;
 
     virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
 
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index b1aa1947..f57d80c 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -37,7 +37,6 @@
 extern int register_android_graphics_Graphics(JNIEnv* env);
 extern int register_android_graphics_ImageDecoder(JNIEnv*);
 extern int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv*);
-extern int register_android_graphics_drawable_LottieDrawable(JNIEnv*);
 extern int register_android_graphics_Interpolator(JNIEnv* env);
 extern int register_android_graphics_MaskFilter(JNIEnv* env);
 extern int register_android_graphics_Movie(JNIEnv* env);
@@ -118,7 +117,6 @@
             REG_JNI(register_android_graphics_HardwareRendererObserver),
             REG_JNI(register_android_graphics_ImageDecoder),
             REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable),
-            REG_JNI(register_android_graphics_drawable_LottieDrawable),
             REG_JNI(register_android_graphics_Interpolator),
             REG_JNI(register_android_graphics_MaskFilter),
             REG_JNI(register_android_graphics_Matrix),
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 07e2fe2..2a20191 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -60,7 +60,6 @@
 typedef std::function<void(uint16_t* text, float* positions)> ReadGlyphFunc;
 
 class AnimatedImageDrawable;
-class LottieDrawable;
 class Bitmap;
 class Paint;
 struct Typeface;
@@ -243,7 +242,6 @@
                                const Paint* paint) = 0;
 
     virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) = 0;
-    virtual void drawLottie(LottieDrawable* lottieDrawable) = 0;
     virtual void drawPicture(const SkPicture& picture) = 0;
 
     /**
diff --git a/libs/hwui/hwui/LottieDrawable.cpp b/libs/hwui/hwui/LottieDrawable.cpp
deleted file mode 100644
index 92dc51e..0000000
--- a/libs/hwui/hwui/LottieDrawable.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LottieDrawable.h"
-
-#include <SkTime.h>
-#include <log/log.h>
-#include <pipeline/skia/SkiaUtils.h>
-
-namespace android {
-
-sk_sp<LottieDrawable> LottieDrawable::Make(sk_sp<skottie::Animation> animation, size_t bytesUsed) {
-    if (animation) {
-        return sk_sp<LottieDrawable>(new LottieDrawable(std::move(animation), bytesUsed));
-    }
-    return nullptr;
-}
-LottieDrawable::LottieDrawable(sk_sp<skottie::Animation> animation, size_t bytesUsed)
-        : mAnimation(std::move(animation)), mBytesUsed(bytesUsed) {}
-
-bool LottieDrawable::start() {
-    if (mRunning) {
-        return false;
-    }
-
-    mRunning = true;
-    return true;
-}
-
-bool LottieDrawable::stop() {
-    bool wasRunning = mRunning;
-    mRunning = false;
-    return wasRunning;
-}
-
-bool LottieDrawable::isRunning() {
-    return mRunning;
-}
-
-// TODO: Check to see if drawable is actually dirty
-bool LottieDrawable::isDirty() {
-    return true;
-}
-
-void LottieDrawable::onDraw(SkCanvas* canvas) {
-    if (mRunning) {
-        const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-
-        nsecs_t t = 0;
-        if (mStartTime == 0) {
-            mStartTime = currentTime;
-        } else {
-            t = currentTime - mStartTime;
-        }
-        double seekTime = std::fmod((double)t * 1e-9, mAnimation->duration());
-        mAnimation->seekFrameTime(seekTime);
-        mAnimation->render(canvas);
-    }
-}
-
-void LottieDrawable::drawStaging(SkCanvas* canvas) {
-    onDraw(canvas);
-}
-
-SkRect LottieDrawable::onGetBounds() {
-    // We do not actually know the bounds, so give a conservative answer.
-    return SkRectMakeLargest();
-}
-
-}  // namespace android
diff --git a/libs/hwui/hwui/LottieDrawable.h b/libs/hwui/hwui/LottieDrawable.h
deleted file mode 100644
index 9cc34bf..0000000
--- a/libs/hwui/hwui/LottieDrawable.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <SkDrawable.h>
-#include <Skottie.h>
-#include <utils/Timers.h>
-
-class SkCanvas;
-
-namespace android {
-
-/**
- * Native component of android.graphics.drawable.LottieDrawable.java.
- * This class can be drawn into Canvas.h and maintains the state needed to drive
- * the animation from the RenderThread.
- */
-class LottieDrawable : public SkDrawable {
-public:
-    static sk_sp<LottieDrawable> Make(sk_sp<skottie::Animation> animation, size_t bytes);
-
-    // Draw to software canvas
-    void drawStaging(SkCanvas* canvas);
-
-    // Returns true if the animation was started; false otherwise (e.g. it was
-    // already running)
-    bool start();
-    // Returns true if the animation was stopped; false otherwise (e.g. it was
-    // already stopped)
-    bool stop();
-    bool isRunning();
-
-    // TODO: Is dirty should take in a time til next frame to determine if it is dirty
-    bool isDirty();
-
-    SkRect onGetBounds() override;
-
-    size_t byteSize() const { return sizeof(*this) + mBytesUsed; }
-
-protected:
-    void onDraw(SkCanvas* canvas) override;
-
-private:
-    LottieDrawable(sk_sp<skottie::Animation> animation, size_t bytes_used);
-
-    sk_sp<skottie::Animation> mAnimation;
-    bool mRunning = false;
-    // The start time for the drawable itself.
-    nsecs_t mStartTime = 0;
-    const size_t mBytesUsed = 0;
-};
-
-}  // namespace android
diff --git a/libs/hwui/jni/LottieDrawable.cpp b/libs/hwui/jni/LottieDrawable.cpp
deleted file mode 100644
index fb6eede..0000000
--- a/libs/hwui/jni/LottieDrawable.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <SkRect.h>
-#include <Skottie.h>
-#include <hwui/Canvas.h>
-#include <hwui/LottieDrawable.h>
-
-#include "GraphicsJNI.h"
-#include "Utils.h"
-
-using namespace android;
-
-static jclass gLottieDrawableClass;
-
-static jlong LottieDrawable_nCreate(JNIEnv* env, jobject, jstring jjson) {
-    const ScopedUtfChars cstr(env, jjson);
-    // TODO(b/259267150) provide more accurate byteSize
-    size_t bytes = strlen(cstr.c_str());
-    auto animation = skottie::Animation::Builder().make(cstr.c_str(), bytes);
-    sk_sp<LottieDrawable> drawable(LottieDrawable::Make(std::move(animation), bytes));
-    if (!drawable) {
-        return 0;
-    }
-    return reinterpret_cast<jlong>(drawable.release());
-}
-
-static void LottieDrawable_destruct(LottieDrawable* drawable) {
-    SkSafeUnref(drawable);
-}
-
-static jlong LottieDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) {
-    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&LottieDrawable_destruct));
-}
-
-static void LottieDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jlong canvasPtr) {
-    auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
-    auto* canvas = reinterpret_cast<Canvas*>(canvasPtr);
-    canvas->drawLottie(drawable);
-}
-
-static jboolean LottieDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
-    auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
-    return drawable->isRunning();
-}
-
-static jboolean LottieDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
-    auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
-    return drawable->start();
-}
-
-static jboolean LottieDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
-    auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
-    return drawable->stop();
-}
-
-static jlong LottieDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
-    auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
-    return drawable->byteSize();
-}
-
-static const JNINativeMethod gLottieDrawableMethods[] = {
-        {"nCreate", "(Ljava/lang/String;)J", (void*)LottieDrawable_nCreate},
-        {"nNativeByteSize", "(J)J", (void*)LottieDrawable_nNativeByteSize},
-        {"nGetNativeFinalizer", "()J", (void*)LottieDrawable_nGetNativeFinalizer},
-        {"nDraw", "(JJ)V", (void*)LottieDrawable_nDraw},
-        {"nIsRunning", "(J)Z", (void*)LottieDrawable_nIsRunning},
-        {"nStart", "(J)Z", (void*)LottieDrawable_nStart},
-        {"nStop", "(J)Z", (void*)LottieDrawable_nStop},
-};
-
-int register_android_graphics_drawable_LottieDrawable(JNIEnv* env) {
-    gLottieDrawableClass = reinterpret_cast<jclass>(
-            env->NewGlobalRef(FindClassOrDie(env, "android/graphics/drawable/LottieDrawable")));
-
-    return android::RegisterMethodsOrDie(env, "android/graphics/drawable/LottieDrawable",
-                                         gLottieDrawableMethods, NELEM(gLottieDrawableMethods));
-}
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 3f4d004..5892308 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -822,6 +822,11 @@
     proxy->notifyCallbackPending();
 }
 
+static void android_view_ThreadedRenderer_notifyExpensiveFrame(JNIEnv*, jclass, jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->notifyExpensiveFrame();
+}
+
 // Plumbs the display density down to DeviceInfo.
 static void android_view_ThreadedRenderer_setDisplayDensityDpi(JNIEnv*, jclass, jint densityDpi) {
     // Convert from dpi to density-independent pixels.
@@ -1000,6 +1005,8 @@
          (void*)android_view_ThreadedRenderer_setRtAnimationsEnabled},
         {"nNotifyCallbackPending", "(J)V",
          (void*)android_view_ThreadedRenderer_notifyCallbackPending},
+        {"nNotifyExpensiveFrame", "(J)V",
+         (void*)android_view_ThreadedRenderer_notifyExpensiveFrame},
 };
 
 static JavaVM* mJvm = nullptr;
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index f0dc5eb..fcfc4f8 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -146,16 +146,6 @@
         }
     }
 
-    for (auto& lottie : mLotties) {
-        // If any animated image in the display list needs updated, then damage the node.
-        if (lottie->isDirty()) {
-            isDirty = true;
-        }
-        if (lottie->isRunning()) {
-            info.out.hasAnimations = true;
-        }
-    }
-
     for (auto& [vectorDrawable, cachedMatrix] : mVectorDrawables) {
         // If any vector drawable in the display list needs update, damage the node.
         if (vectorDrawable->isDirty()) {
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 39217fc..2a67734 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -22,7 +22,6 @@
 #include "RenderNodeDrawable.h"
 #include "TreeInfo.h"
 #include "hwui/AnimatedImageDrawable.h"
-#include "hwui/LottieDrawable.h"
 #include "utils/LinearAllocator.h"
 #include "utils/Pair.h"
 
@@ -187,8 +186,6 @@
         return mHasHolePunches;
     }
 
-    // TODO(b/257304231): create common base class for Lotties and AnimatedImages
-    std::vector<LottieDrawable*> mLotties;
     std::vector<AnimatedImageDrawable*> mAnimatedImages;
     DisplayListData mDisplayList;
 
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 08f0291..c9d79ab 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -189,11 +189,6 @@
 #endif
 }
 
-void SkiaRecordingCanvas::drawLottie(LottieDrawable* lottie) {
-    drawDrawable(lottie);
-    mDisplayList->mLotties.push_back(lottie);
-}
-
 void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
     mRecorder.drawVectorDrawable(tree);
     SkMatrix mat;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index c823d8d..7844e2c 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -78,7 +78,6 @@
                             uirenderer::CanvasPropertyPaint* paint) override;
     virtual void drawRipple(const RippleDrawableParams& params) override;
 
-    virtual void drawLottie(LottieDrawable* lottieDrawable) override;
     virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
 
     virtual void enableZ(bool enableZ) override;
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 1c76884..23611ef 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -19,7 +19,6 @@
 #include <GrContextOptions.h>
 #include <SkExecutor.h>
 #include <SkGraphics.h>
-#include <SkMathPriv.h>
 #include <math.h>
 #include <utils/Trace.h>
 
@@ -47,13 +46,23 @@
     setupCacheLimits();
 }
 
+static inline int countLeadingZeros(uint32_t mask) {
+    // __builtin_clz(0) is undefined, so we have to detect that case.
+    return mask ? __builtin_clz(mask) : 32;
+}
+
+// Return the smallest power-of-2 >= n.
+static inline uint32_t nextPowerOfTwo(uint32_t n) {
+    return n ? (1 << (32 - countLeadingZeros(n - 1))) : 1;
+}
+
 void CacheManager::setupCacheLimits() {
     mMaxResourceBytes = mMaxSurfaceArea * mMemoryPolicy.surfaceSizeMultiplier;
     mBackgroundResourceBytes = mMaxResourceBytes * mMemoryPolicy.backgroundRetentionPercent;
     // This sets the maximum size for a single texture atlas in the GPU font cache. If
     // necessary, the cache can allocate additional textures that are counted against the
     // total cache limits provided to Skia.
-    mMaxGpuFontAtlasBytes = GrNextSizePow2(mMaxSurfaceArea);
+    mMaxGpuFontAtlasBytes = nextPowerOfTwo(mMaxSurfaceArea);
     // This sets the maximum size of the CPU font cache to be at least the same size as the
     // total number of GPU font caches (i.e. 4 separate GPU atlases).
     mMaxCpuFontCacheBytes = std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit());
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b769f8d..f223137 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -1021,6 +1021,10 @@
     mHintSessionWrapper.sendLoadResetHint();
 }
 
+void CanvasContext::sendLoadIncreaseHint() {
+    mHintSessionWrapper.sendLoadIncreaseHint();
+}
+
 void CanvasContext::setSyncDelayDuration(nsecs_t duration) {
     mSyncDelayDuration = duration;
 }
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 3f796d9..a274d2f 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -223,6 +223,8 @@
 
     void sendLoadResetHint();
 
+    void sendLoadIncreaseHint();
+
     void setSyncDelayDuration(nsecs_t duration);
 
 private:
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index 94c9d94..dece548 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -158,6 +158,11 @@
     mLastFrameNotification = now;
 }
 
+void HintSessionWrapper::sendLoadIncreaseHint() {
+    if (!useHintSession()) return;
+    gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_UP));
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index fcbc101..c0f7a57 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -33,6 +33,7 @@
     void updateTargetWorkDuration(long targetDurationNanos);
     void reportActualWorkDuration(long actualDurationNanos);
     void sendLoadResetHint();
+    void sendLoadIncreaseHint();
 
 private:
     bool useHintSession();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ed01e32..5edb0b1 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -254,6 +254,10 @@
     mRenderThread.queue().post([this]() { mContext->sendLoadResetHint(); });
 }
 
+void RenderProxy::notifyExpensiveFrame() {
+    mRenderThread.queue().post([this]() { mContext->sendLoadIncreaseHint(); });
+}
+
 void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
     mRenderThread.queue().runSync([&]() {
         std::lock_guard lock(mRenderThread.getJankDataMutex());
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 17cf665..2aafe76 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -112,6 +112,7 @@
     void stopDrawing();
     void notifyFramePending();
     void notifyCallbackPending();
+    void notifyExpensiveFrame();
 
     void dumpProfileInfo(int fd, int dumpFlags);
     // Not exported, only used for testing
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 27c2a98..ac920d2 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -1170,6 +1170,11 @@
      * in Tuner 2.0 or higher version. Unsupported version will cause no-op. Use {@link
      * TunerVersionChecker#getTunerVersion()} to get the version information.
      *
+     * <p>Tuning with {@link
+     * android.media.tv.tuner.frontend.IptvFrontendSettings} is only supported
+     * in Tuner 3.0 or higher version. Unsupported version will cause no-op. Use {@link
+     * TunerVersionChecker#getTunerVersion()} to get the version information.
+     *
      * @param settings Signal delivery information the frontend uses to
      *                 search and lock the signal.
      * @return result status of tune operation.
@@ -1198,6 +1203,12 @@
                     return RESULT_UNAVAILABLE;
                 }
             }
+            if (mFrontendType == FrontendSettings.TYPE_IPTV) {
+                if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+                        TunerVersionChecker.TUNER_VERSION_3_0, "Tuner with IPTV Frontend")) {
+                    return RESULT_UNAVAILABLE;
+                }
+            }
 
             if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, mFrontendLock)) {
                 mFrontendInfo = null;
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
index 2f45a70..0a1ecee 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -38,7 +38,7 @@
     /** @hide */
     @IntDef(prefix = "TYPE_",
             value = {TYPE_UNDEFINED, TYPE_ANALOG, TYPE_ATSC, TYPE_ATSC3, TYPE_DVBC, TYPE_DVBS,
-                    TYPE_DVBT, TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT, TYPE_DTMB})
+                    TYPE_DVBT, TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT, TYPE_DTMB, TYPE_IPTV})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Type {}
 
@@ -86,7 +86,10 @@
      * Digital Terrestrial Multimedia Broadcast standard (DTMB) frontend type.
      */
     public static final int TYPE_DTMB = FrontendType.DTMB;
-
+    /**
+     * Internet Protocol (IPTV) frontend type.
+     */
+    public static final int TYPE_IPTV = FrontendType.IPTV;
 
     /** @hide */
     @LongDef(prefix = "FEC_",
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 9fbea72..fd677ac 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -23,6 +23,7 @@
 import android.annotation.SystemApi;
 import android.media.tv.tuner.Lnb;
 import android.media.tv.tuner.TunerVersionChecker;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
@@ -57,7 +58,10 @@
             FRONTEND_STATUS_TYPE_IS_MISO_ENABLED, FRONTEND_STATUS_TYPE_IS_LINEAR,
             FRONTEND_STATUS_TYPE_IS_SHORT_FRAMES_ENABLED, FRONTEND_STATUS_TYPE_ISDBT_MODE,
             FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG, FRONTEND_STATUS_TYPE_STREAM_IDS,
-            FRONTEND_STATUS_TYPE_DVBT_CELL_IDS, FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO})
+            FRONTEND_STATUS_TYPE_DVBT_CELL_IDS, FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO,
+            FRONTEND_STATUS_TYPE_IPTV_CONTENT_URL, FRONTEND_STATUS_TYPE_IPTV_PACKETS_LOST,
+            FRONTEND_STATUS_TYPE_IPTV_PACKETS_RECEIVED, FRONTEND_STATUS_TYPE_IPTV_WORST_JITTER_MS,
+            FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS})
     @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendStatusType {}
 
@@ -271,6 +275,36 @@
             android.hardware.tv.tuner.FrontendStatusType.DVBT_CELL_IDS;
 
     /**
+     * IPTV content URL.
+     */
+    public static final int FRONTEND_STATUS_TYPE_IPTV_CONTENT_URL =
+            android.hardware.tv.tuner.FrontendStatusType.IPTV_CONTENT_URL;
+
+    /**
+     * IPTV packets lost.
+     */
+    public static final int FRONTEND_STATUS_TYPE_IPTV_PACKETS_LOST =
+            android.hardware.tv.tuner.FrontendStatusType.IPTV_PACKETS_LOST;
+
+    /**
+     * IPTV packets received.
+     */
+    public static final int FRONTEND_STATUS_TYPE_IPTV_PACKETS_RECEIVED =
+            android.hardware.tv.tuner.FrontendStatusType.IPTV_PACKETS_RECEIVED;
+
+    /**
+     * IPTV worst jitter.
+     */
+    public static final int FRONTEND_STATUS_TYPE_IPTV_WORST_JITTER_MS =
+            android.hardware.tv.tuner.FrontendStatusType.IPTV_WORST_JITTER_MS;
+
+    /**
+     * IPTV average jitter.
+     */
+    public static final int FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS =
+            android.hardware.tv.tuner.FrontendStatusType.IPTV_AVERAGE_JITTER_MS;
+
+    /**
      * All PLP information in a frequency band for ATSC-3.0 frontend, which includes both tuned and
      * not tuned PLPs for currently watching service.
      */
@@ -519,6 +553,11 @@
     private int[] mStreamIds;
     private int[] mDvbtCellIds;
     private Atsc3PlpInfo[] mAllPlpInfo;
+    private String mIptvContentUrl;
+    private Long mIptvPacketsLost;
+    private Long mIptvPacketsReceived;
+    private Integer mIptvWorstJitterMs;
+    private Integer mIptvAverageJitterMs;
 
     // Constructed and fields set by JNI code.
     private FrontendStatus() {
@@ -1144,4 +1183,94 @@
             return mUec;
         }
     }
+
+    /**
+     * Gets the IPTV content URL.
+     *
+     * @return A String URL in the format protocol://ip:port (udp://127.0.0.1:3000).
+     *
+     * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+     * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+     */
+    @NonNull
+    public String getIptvContentUrl() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_3_0, "IptvContentUrl status");
+        if (mIptvContentUrl == null) {
+            throw new IllegalStateException("IptvContentUrl status is empty");
+        }
+        return mIptvContentUrl;
+    }
+
+    /**
+     * Gets the number of packets lost.
+     *
+     * @return A long value representing the number of packets lost in transmission.
+     *
+     * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+     * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+     */
+    @IntRange(from = 0)
+    public long getIptvPacketsLost() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_3_0, "IptvPacketsLost status");
+        if (mIptvPacketsLost == null) {
+            throw new IllegalStateException("IptvPacketsLost status is empty");
+        }
+        return mIptvPacketsLost;
+    }
+
+    /**
+     * Gets the number of packets received.
+     *
+     * @return A long value representing the number of packets received.
+     *
+     * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+     * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+     */
+    @IntRange(from = 0)
+    public long getIptvPacketsReceived() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_3_0, "IptvPacketsReceived status");
+        if (mIptvPacketsReceived == null) {
+            throw new IllegalStateException("IptvPacketsReceived status is empty");
+        }
+        return mIptvPacketsReceived;
+    }
+
+    /**
+     * Gets the worst jitter.
+     *
+     * @return An integer representing worst jitter recorded (in milliseconds).
+     *
+     * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+     * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+     */
+    @IntRange(from = 0)
+    public int getIptvWorstJitterMillis() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_3_0, "IptvWorstJitterMs status");
+        if (mIptvWorstJitterMs == null) {
+            throw new IllegalStateException("IptvWorstJitterMs status is empty");
+        }
+        return mIptvWorstJitterMs;
+    }
+
+    /**
+     * Gets the average jitter.
+     *
+     * @return An integer representing average jitter recorded (in milliseconds).
+     *
+     * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+     * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+     */
+    @IntRange(from = 0)
+    public int getIptvAverageJitterMillis() {
+        TunerVersionChecker.checkHigherOrEqualVersionTo(
+                TunerVersionChecker.TUNER_VERSION_3_0, "IptvAverageJitterMs status");
+        if (mIptvAverageJitterMs == null) {
+            throw new IllegalStateException("IptvAverageJitterMs status is empty");
+        }
+        return mIptvAverageJitterMs;
+    }
 }
diff --git a/media/java/android/media/tv/tuner/frontend/IptvFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettings.java
new file mode 100644
index 0000000..fb11ea9
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettings.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.Size;
+import android.annotation.SystemApi;
+import android.hardware.tv.tuner.FrontendIptvSettingsIgmp;
+import android.hardware.tv.tuner.FrontendIptvSettingsProtocol;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Frontend settings for IPTV.
+ *
+ * @hide
+ */
+@SystemApi
+public class IptvFrontendSettings extends FrontendSettings {
+    /** @hide */
+    @IntDef(prefix = "PROTOCOL_",
+            value = {PROTOCOL_UNDEFINED, PROTOCOL_UDP, PROTOCOL_RTP})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Protocol {}
+
+    /**
+     * IP protocol type UNDEFINED.
+     */
+    public static final int PROTOCOL_UNDEFINED = FrontendIptvSettingsProtocol.UNDEFINED;
+
+    /**
+     * IP protocol type UDP (User Datagram Protocol).
+     */
+    public static final int PROTOCOL_UDP = FrontendIptvSettingsProtocol.UDP;
+
+    /**
+     * IP protocol type RTP (Real-time Transport Protocol).
+     */
+    public static final int PROTOCOL_RTP = FrontendIptvSettingsProtocol.RTP;
+
+    /** @hide */
+    @IntDef(prefix = "IGMP_",
+            value = {IGMP_UNDEFINED, IGMP_V1, IGMP_V2, IGMP_V3})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Igmp {}
+
+    /**
+     * IGMP (Internet Group Management Protocol) UNDEFINED.
+     */
+    public static final int IGMP_UNDEFINED = FrontendIptvSettingsIgmp.UNDEFINED;
+
+    /**
+     * IGMP (Internet Group Management Protocol) V1.
+     */
+    public static final int IGMP_V1 = FrontendIptvSettingsIgmp.V1;
+
+    /**
+     * IGMP (Internet Group Management Protocol) V2.
+     */
+    public static final int IGMP_V2 = FrontendIptvSettingsIgmp.V2;
+
+    /**
+     * IGMP (Internet Group Management Protocol) V3.
+     */
+    public static final int IGMP_V3 = FrontendIptvSettingsIgmp.V3;
+
+    private final byte[] mSrcIpAddress;
+    private final byte[] mDstIpAddress;
+    private final int mSrcPort;
+    private final int mDstPort;
+    private final IptvFrontendSettingsFec mFec;
+    private final int mProtocol;
+    private final int mIgmp;
+    private final long mBitrate;
+    private final String mContentUrl;
+
+    public IptvFrontendSettings(@NonNull byte[] srcIpAddress, @NonNull byte[] dstIpAddress,
+            int srcPort, int dstPort, @NonNull IptvFrontendSettingsFec fec, int protocol, int igmp,
+            long bitrate, @NonNull String contentUrl) {
+        super(0);
+        mSrcIpAddress = srcIpAddress;
+        mDstIpAddress = dstIpAddress;
+        mSrcPort = srcPort;
+        mDstPort = dstPort;
+        mFec = fec;
+        mProtocol = protocol;
+        mIgmp = igmp;
+        mBitrate = bitrate;
+        mContentUrl = contentUrl;
+    }
+
+    /**
+     * Gets the source IP address.
+     */
+    @Size(min = 4, max = 16)
+    @NonNull
+    public byte[] getSrcIpAddress() {
+        return mSrcIpAddress;
+    }
+
+    /**
+     * Gets the destination IP address.
+     */
+    @Size(min = 4, max = 16)
+    @NonNull
+    public byte[] getDstIpAddress() {
+        return mDstIpAddress;
+    }
+
+    /**
+     * Gets the source port.
+     */
+    public int getSrcPort() {
+        return mSrcPort;
+    }
+
+    /**
+     * Gets the destination port.
+     */
+    public int getDstPort() {
+        return mDstPort;
+    }
+
+    /**
+     * Gets FEC (Forward Error Correction).
+     */
+    @Nullable
+    public IptvFrontendSettingsFec getFec() {
+        return mFec;
+    }
+
+    /**
+     * Gets the protocol.
+     */
+    @Protocol
+    public int getProtocol() {
+        return mProtocol;
+    }
+
+    /**
+     * Gets the IGMP (Internet Group Management Protocol).
+     */
+    @Igmp
+    public int getIgmp() {
+        return mIgmp;
+    }
+
+    /**
+     * Gets the bitrate.
+     */
+    @IntRange(from = 0)
+    public long getBitrate() {
+        return mBitrate;
+    }
+
+    /**
+     * Gets the contentUrl
+     * contentUrl is a source URL in the format protocol://ip:port containing data
+     */
+    @NonNull
+    public String getContentUrl() {
+        return mContentUrl;
+    }
+
+    /**
+     * Creates a builder for {@link IptvFrontendSettings}.
+     */
+    @NonNull
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link IptvFrontendSettings}.
+     */
+    public static final class Builder {
+        private byte[] mSrcIpAddress = {0, 0, 0, 0};
+        private byte[] mDstIpAddress = {0, 0, 0, 0};
+        private int mSrcPort = 0;
+        private int mDstPort = 0;
+        private IptvFrontendSettingsFec mFec = null;
+        private int mProtocol = FrontendIptvSettingsProtocol.UNDEFINED;
+        private int mIgmp = FrontendIptvSettingsIgmp.UNDEFINED;
+        private long mBitrate = 0;
+        private String mContentUrl = "";
+
+        private Builder() {
+        }
+
+        /**
+         * Sets the source IP address.
+         *
+         * <p>Default value is 0.0.0.0, an invalid IP address.
+         */
+        @NonNull
+        public Builder setSrcIpAddress(@NonNull  byte[] srcIpAddress) {
+            mSrcIpAddress = srcIpAddress;
+            return this;
+        }
+
+        /**
+         * Sets the destination IP address.
+         *
+         * <p>Default value is 0.0.0.0, an invalid IP address.
+         */
+        @NonNull
+        public Builder setDstIpAddress(@NonNull  byte[] dstIpAddress) {
+            mDstIpAddress = dstIpAddress;
+            return this;
+        }
+
+        /**
+         * Sets the source IP port.
+         *
+         * <p>Default value is 0.
+         */
+        @NonNull
+        public Builder setSrcPort(int srcPort) {
+            mSrcPort = srcPort;
+            return this;
+        }
+
+        /**
+         * Sets the destination IP port.
+         *
+         * <p>Default value is 0.
+         */
+        @NonNull
+        public Builder setDstPort(int dstPort) {
+            mDstPort = dstPort;
+            return this;
+        }
+
+        /**
+         * Sets the FEC (Forward Error Correction).
+         *
+         * <p>Default value is {@code null}.
+         */
+        @NonNull
+        public Builder setFec(@Nullable IptvFrontendSettingsFec fec) {
+            mFec = fec;
+            return this;
+        }
+
+        /**
+         * Sets the protocol.
+         *
+         * <p>Default value is {@link #PROTOCOL_UNDEFINED}.
+         */
+        @NonNull
+        public Builder setProtocol(@Protocol int protocol) {
+            mProtocol = protocol;
+            return this;
+        }
+
+        /**
+         * Sets the IGMP (Internet Group Management Protocol).
+         *
+         * <p>Default value is {@link #IGMP_UNDEFINED}.
+         */
+        @NonNull
+        public Builder setIgmp(@Igmp int igmp) {
+            mIgmp = igmp;
+            return this;
+        }
+
+        /**
+         * Sets the bitrate.
+         *
+         * <p>Default value is 0.
+         */
+        @NonNull
+        public Builder setBitrate(@IntRange(from = 0) long bitrate) {
+            mBitrate = bitrate;
+            return this;
+        }
+
+        /**
+         * Sets the contentUrl.
+         *
+         * <p>Default value is "".
+         */
+        @NonNull
+        public Builder setContentUrl(@NonNull String contentUrl) {
+            mContentUrl = contentUrl;
+            return this;
+        }
+
+        /**
+         * Builds a {@link IptvFrontendSettings} object.
+         */
+        @NonNull
+        public IptvFrontendSettings build() {
+            return new IptvFrontendSettings(mSrcIpAddress, mDstIpAddress, mSrcPort,
+                    mDstPort, mFec, mProtocol, mIgmp, mBitrate, mContentUrl);
+        }
+    }
+
+    @Override
+    public int getType() {
+        return FrontendSettings.TYPE_IPTV;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/IptvFrontendSettingsFec.java b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettingsFec.java
new file mode 100644
index 0000000..699d615
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettingsFec.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.hardware.tv.tuner.FrontendIptvSettingsFecType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * FEC (Forward Error Correction) for IPTV.
+ *
+ * @hide
+ */
+@SystemApi
+public class IptvFrontendSettingsFec {
+    /** @hide */
+    @IntDef(prefix = "FEC_TYPE_",
+            value = {FEC_TYPE_UNDEFINED, FEC_TYPE_COLUMN, FEC_TYPE_ROW, FEC_TYPE_COLUMN_ROW})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FecType {}
+
+    /**
+     * FEC (Forward Error Correction) type UNDEFINED.
+     */
+    public static final int FEC_TYPE_UNDEFINED = FrontendIptvSettingsFecType.UNDEFINED;
+
+    /**
+     * FEC (Forward Error Correction) type Column.
+     */
+    public static final int FEC_TYPE_COLUMN = FrontendIptvSettingsFecType.COLUMN;
+
+    /**
+     * FEC (Forward Error Correction) type ROW.
+     */
+    public static final int FEC_TYPE_ROW = FrontendIptvSettingsFecType.ROW;
+
+    /**
+     * FEC (Forward Error Correction) type Column Row.
+     */
+    public static final int FEC_TYPE_COLUMN_ROW = FrontendIptvSettingsFecType.COLUMN_ROW;
+
+    private final int mFecType;
+    private final int mFecRowNum;
+    private final int mFecColNum;
+
+    public IptvFrontendSettingsFec(@FecType int fecType, int fecRowNum, int fecColNum) {
+        mFecType = fecType;
+        mFecRowNum = fecRowNum;
+        mFecColNum = fecColNum;
+    }
+
+    /**
+     * Gets the FEC (Forward Error Correction) type.
+     */
+    @FecType
+    public int getFecType() {
+        return mFecType;
+    }
+
+    /**
+     * Get the FEC (Forward Error Correction) row number.
+     */
+    @IntRange(from = 0)
+    public int getFecRowNum() {
+        return mFecRowNum;
+    }
+
+    /**
+     * Gets the FEC (Forward Error Correction) column number.
+     */
+    @IntRange(from = 0)
+    public int getFecColNum() {
+        return mFecColNum;
+    }
+
+    /**
+     * Creates a builder for {@link IptvFrontendSettingsFec}.
+     */
+    @NonNull
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link IptvFrontendSettingsFec}.
+     */
+    public static final class Builder {
+        private int mFecType;
+        private int mFecRowNum;
+        private int mFecColNum;
+
+        private Builder() {
+        }
+
+        /**
+         * Sets the FEC (Forward Error Correction) type
+         */
+        @NonNull
+        public Builder setFecType(@FecType int fecType) {
+            mFecType = fecType;
+            return this;
+        }
+        /**
+         * Sets the FEC (Forward Error Correction) row number.
+         */
+        @NonNull
+        public Builder setFecRowNum(@IntRange(from = 0) int fecRowNum) {
+            mFecRowNum = fecRowNum;
+            return this;
+        }
+        /**
+         * Sets the FEC (Forward Error Correction) column number.
+         */
+        @NonNull
+        public Builder setFecColNum(@IntRange(from = 0) int fecColNum) {
+            mFecColNum = fecColNum;
+            return this;
+        }
+
+        /**
+         * Builds a {@link IptvFrontendSettingsFec} object.
+         */
+        @NonNull
+        public IptvFrontendSettingsFec build() {
+            return new IptvFrontendSettingsFec(mFecType, mFecRowNum, mFecColNum);
+        }
+    }
+}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index a73725b..35ee3ee9 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -490,9 +490,11 @@
 }
 
 void MediaEvent::finalize() {
-    if (mAvHandleRefCnt == 0 && mFilterClient != nullptr) {
-        mFilterClient->releaseAvHandle(
-                mAvHandle, mDataIdRefCnt == 0 ? mDataId : 0);
+    if (mAvHandleRefCnt == 0) {
+        if (mFilterClient != nullptr) {
+            mFilterClient->releaseAvHandle(
+                    mAvHandle, mDataIdRefCnt == 0 ? mDataId : 0);
+        }
         native_handle_close(mAvHandle);
     }
 }
@@ -2825,6 +2827,52 @@
                 env->DeleteLocalRef(plpClazz);
                 break;
             }
+            case FrontendStatus::Tag::iptvContentUrl: {
+                jfieldID field = env->GetFieldID(clazz, "mIptvContentUrl", "Ljava/lang/String;");
+                std::string iptvContentUrl = s.get<FrontendStatus::Tag::iptvContentUrl>();
+                jstring iptvContentUrlUtf8 = env->NewStringUTF(iptvContentUrl.c_str());
+                env->SetObjectField(statusObj, field, iptvContentUrlUtf8);
+                env->DeleteLocalRef(iptvContentUrlUtf8);
+                break;
+            }
+            case FrontendStatus::Tag::iptvPacketsLost: {
+                jfieldID field = env->GetFieldID(clazz, "mIptvPacketsLost", "Ljava/lang/Long;");
+                jobject newLongObj =
+                        env->NewObject(longClazz, initLong,
+                                       s.get<FrontendStatus::Tag::iptvPacketsLost>());
+                env->SetObjectField(statusObj, field, newLongObj);
+                env->DeleteLocalRef(newLongObj);
+                break;
+            }
+            case FrontendStatus::Tag::iptvPacketsReceived: {
+                jfieldID field = env->GetFieldID(clazz, "mIptvPacketsReceived", "Ljava/lang/Long;");
+                jobject newLongObj =
+                        env->NewObject(longClazz, initLong,
+                                       s.get<FrontendStatus::Tag::iptvPacketsReceived>());
+                env->SetObjectField(statusObj, field, newLongObj);
+                env->DeleteLocalRef(newLongObj);
+                break;
+            }
+            case FrontendStatus::Tag::iptvWorstJitterMs: {
+                jfieldID field = env->GetFieldID(clazz, "mIptvWorstJitterMs",
+                                                 "Ljava/lang/Integer;");
+                jobject newIntegerObj =
+                        env->NewObject(intClazz, initInt,
+                                       s.get<FrontendStatus::Tag::iptvWorstJitterMs>());
+                env->SetObjectField(statusObj, field, newIntegerObj);
+                env->DeleteLocalRef(newIntegerObj);
+                break;
+            }
+            case FrontendStatus::Tag::iptvAverageJitterMs: {
+                jfieldID field = env->GetFieldID(clazz, "mIptvAverageJitterMs",
+                                                 "Ljava/lang/Integer;");
+                jobject newIntegerObj =
+                        env->NewObject(intClazz, initInt,
+                                       s.get<FrontendStatus::Tag::iptvAverageJitterMs>());
+                env->SetObjectField(statusObj, field, newIntegerObj);
+                env->DeleteLocalRef(newIntegerObj);
+                break;
+            }
         }
     }
     return statusObj;
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index e4b9b5d..987b23f 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -1,9 +1,9 @@
 LIBANDROID {
   global:
-    AActivityManager_addUidImportanceListener; # apex # introduced=31
-    AActivityManager_removeUidImportanceListener; # apex # introduced=31
-    AActivityManager_isUidActive; # apex # introduced=31
-    AActivityManager_getUidImportance; # apex # introduced=31
+    AActivityManager_addUidImportanceListener; # systemapi # introduced=31
+    AActivityManager_removeUidImportanceListener; # systemapi # introduced=31
+    AActivityManager_isUidActive; # systemapi # introduced=31
+    AActivityManager_getUidImportance; # systemapi # introduced=31
     AAssetDir_close;
     AAssetDir_getNextFileName;
     AAssetDir_rewind;
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
index a2fb101..3b9bf47 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
@@ -33,7 +33,6 @@
 fun DisposableBroadcastReceiverAsUser(
     intentFilter: IntentFilter,
     userHandle: UserHandle,
-    onStart: () -> Unit = {},
     onReceive: (Intent) -> Unit,
 ) {
     val context = LocalContext.current
@@ -49,7 +48,6 @@
             context.registerReceiverAsUser(
                 broadcastReceiver, userHandle, intentFilter, null, null
             )
-            onStart()
         },
         onStop = {
             context.unregisterReceiver(broadcastReceiver)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
index ce0c551..5342def 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
@@ -29,19 +29,11 @@
 import kotlinx.coroutines.runBlocking
 
 /**
- * The config used to load the App List.
- */
-data class AppListConfig(
-    val userId: Int,
-    val showInstantApps: Boolean,
-)
-
-/**
  * The repository to load the App List data.
  */
 internal interface AppListRepository {
     /** Loads the list of [ApplicationInfo]. */
-    suspend fun loadApps(config: AppListConfig): List<ApplicationInfo>
+    suspend fun loadApps(userId: Int, showInstantApps: Boolean): List<ApplicationInfo>
 
     /** Gets the flow of predicate that could used to filter system app. */
     fun showSystemPredicate(
@@ -50,7 +42,7 @@
     ): Flow<(app: ApplicationInfo) -> Boolean>
 
     /** Gets the system app package names. */
-    fun getSystemPackageNamesBlocking(config: AppListConfig): Set<String>
+    fun getSystemPackageNamesBlocking(userId: Int, showInstantApps: Boolean): Set<String>
 }
 
 /**
@@ -59,15 +51,21 @@
 object AppListRepositoryUtil {
     /** Gets the system app package names. */
     @JvmStatic
-    fun getSystemPackageNames(context: Context, config: AppListConfig): Set<String> {
-        return AppListRepositoryImpl(context).getSystemPackageNamesBlocking(config)
-    }
+    fun getSystemPackageNames(
+        context: Context,
+        userId: Int,
+        showInstantApps: Boolean,
+    ): Set<String> =
+        AppListRepositoryImpl(context).getSystemPackageNamesBlocking(userId, showInstantApps)
 }
 
 internal class AppListRepositoryImpl(private val context: Context) : AppListRepository {
     private val packageManager = context.packageManager
 
-    override suspend fun loadApps(config: AppListConfig): List<ApplicationInfo> = coroutineScope {
+    override suspend fun loadApps(
+        userId: Int,
+        showInstantApps: Boolean,
+    ): List<ApplicationInfo> = coroutineScope {
         val hiddenSystemModulesDeferred = async {
             packageManager.getInstalledModules(0)
                 .filter { it.isHidden }
@@ -82,12 +80,12 @@
                 PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong()
         )
         val installedApplicationsAsUser =
-            packageManager.getInstalledApplicationsAsUser(flags, config.userId)
+            packageManager.getInstalledApplicationsAsUser(flags, userId)
 
         val hiddenSystemModules = hiddenSystemModulesDeferred.await()
         val hideWhenDisabledPackages = hideWhenDisabledPackagesDeferred.await()
         installedApplicationsAsUser.filter { app ->
-            app.isInAppList(config.showInstantApps, hiddenSystemModules, hideWhenDisabledPackages)
+            app.isInAppList(showInstantApps, hiddenSystemModules, hideWhenDisabledPackages)
         }
     }
 
@@ -97,18 +95,17 @@
     ): Flow<(app: ApplicationInfo) -> Boolean> =
         userIdFlow.combine(showSystemFlow, ::showSystemPredicate)
 
-    override fun getSystemPackageNamesBlocking(config: AppListConfig) = runBlocking {
-        getSystemPackageNames(config)
-    }
+    override fun getSystemPackageNamesBlocking(userId: Int, showInstantApps: Boolean) =
+        runBlocking { getSystemPackageNames(userId, showInstantApps) }
 
-    private suspend fun getSystemPackageNames(config: AppListConfig): Set<String> =
-            coroutineScope {
-                val loadAppsDeferred = async { loadApps(config) }
-                val homeOrLauncherPackages = loadHomeOrLauncherPackages(config.userId)
-                val showSystemPredicate =
-                        { app: ApplicationInfo -> isSystemApp(app, homeOrLauncherPackages) }
-                loadAppsDeferred.await().filter(showSystemPredicate).map { it.packageName }.toSet()
-            }
+    private suspend fun getSystemPackageNames(userId: Int, showInstantApps: Boolean): Set<String> =
+        coroutineScope {
+            val loadAppsDeferred = async { loadApps(userId, showInstantApps) }
+            val homeOrLauncherPackages = loadHomeOrLauncherPackages(userId)
+            val showSystemPredicate =
+                { app: ApplicationInfo -> isSystemApp(app, homeOrLauncherPackages) }
+            loadAppsDeferred.await().filter(showSystemPredicate).map { it.packageName }.toSet()
+        }
 
     private suspend fun showSystemPredicate(
         userId: Int,
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
index 6cd1c0d..8896042 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
@@ -26,6 +26,7 @@
 import com.android.settingslib.spa.framework.util.asyncMapItem
 import com.android.settingslib.spa.framework.util.waitFirst
 import com.android.settingslib.spa.widget.ui.SpinnerOption
+import com.android.settingslib.spaprivileged.template.app.AppListConfig
 import java.util.concurrent.ConcurrentHashMap
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -34,8 +35,8 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.shareIn
@@ -78,32 +79,77 @@
     private val labelMap = ConcurrentHashMap<String, String>()
     private val scope = viewModelScope + Dispatchers.IO
 
-    private val userIdFlow = appListConfig.flow.map { it.userId }
+    private val userSubGraphsFlow = appListConfig.flow.map { config ->
+        config.userIds.map { userId -> UserSubGraph(userId, config.showInstantApps) }
+    }.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
 
-    private val appsStateFlow = MutableStateFlow<List<ApplicationInfo>?>(null)
+    private inner class UserSubGraph(
+        private val userId: Int,
+        private val showInstantApps: Boolean,
+    ) {
+        private val userIdFlow = flowOf(userId)
 
-    private val recordListFlow = listModel.flow
-        .flatMapLatest { it.transform(userIdFlow, appsStateFlow.filterNotNull()) }
-        .shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
+        private val appsStateFlow = MutableStateFlow<List<ApplicationInfo>?>(null)
 
-    private val systemFilteredFlow =
-        appListRepository.showSystemPredicate(userIdFlow, showSystem.flow)
-            .combine(recordListFlow) { showAppPredicate, recordList ->
-                recordList.filter { showAppPredicate(it.app) }
+        val recordListFlow = listModel.flow
+            .flatMapLatest { it.transform(userIdFlow, appsStateFlow.filterNotNull()) }
+            .shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
+
+        private val systemFilteredFlow =
+            appListRepository.showSystemPredicate(userIdFlow, showSystem.flow)
+                .combine(recordListFlow) { showAppPredicate, recordList ->
+                    recordList.filter { showAppPredicate(it.app) }
+                }
+                .shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
+
+        val listModelFilteredFlow = optionFlow.filterNotNull().flatMapLatest { option ->
+            listModel.flow.flatMapLatest { listModel ->
+                listModel.filter(this.userIdFlow, option, this.systemFilteredFlow)
             }
+        }.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
+
+        fun reloadApps() {
+            scope.launch {
+                appsStateFlow.value = appListRepository.loadApps(userId, showInstantApps)
+            }
+        }
+    }
+
+    private val combinedRecordListFlow = userSubGraphsFlow.flatMapLatest { userSubGraphList ->
+        combine(userSubGraphList.map { it.recordListFlow }) { it.toList().flatten() }
+    }.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
 
     override val spinnerOptionsFlow =
-        recordListFlow.combine(listModel.flow) { recordList, listModel ->
+        combinedRecordListFlow.combine(listModel.flow) { recordList, listModel ->
             listModel.getSpinnerOptions(recordList)
-        }
+        }.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
 
-    override val appListDataFlow = optionFlow.filterNotNull().flatMapLatest(::filterAndSort)
-        .combine(searchQuery.flow) { appListData, searchQuery ->
+    private val appEntryListFlow = userSubGraphsFlow.flatMapLatest { userSubGraphList ->
+        combine(userSubGraphList.map { it.listModelFilteredFlow }) { it.toList().flatten() }
+    }.asyncMapItem { record ->
+        val label = getLabel(record.app)
+        AppEntry(
+            record = record,
+            label = label,
+            labelCollationKey = collator.getCollationKey(label),
+        )
+    }
+
+    override val appListDataFlow =
+        combine(
+            appEntryListFlow,
+            listModel.flow,
+            optionFlow.filterNotNull(),
+        ) { appEntries, listModel, option ->
+            AppListData(
+                appEntries = appEntries.sortedWith(listModel.getComparator(option)),
+                option = option,
+            )
+        }.combine(searchQuery.flow) { appListData, searchQuery ->
             appListData.filter {
                 it.label.contains(other = searchQuery, ignoreCase = true)
             }
-        }
-        .shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
+        }.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
 
     init {
         scheduleOnFirstLoaded()
@@ -111,30 +157,16 @@
 
     fun reloadApps() {
         scope.launch {
-            appsStateFlow.value = appListRepository.loadApps(appListConfig.flow.first())
+            userSubGraphsFlow.collect { userSubGraphList ->
+                for (userSubGraph in userSubGraphList) {
+                    userSubGraph.reloadApps()
+                }
+            }
         }
     }
 
-    private fun filterAndSort(option: Int) = listModel.flow.flatMapLatest { listModel ->
-        listModel.filter(userIdFlow, option, systemFilteredFlow)
-            .asyncMapItem { record ->
-                val label = getLabel(record.app)
-                AppEntry(
-                    record = record,
-                    label = label,
-                    labelCollationKey = collator.getCollationKey(label),
-                )
-            }
-            .map { appEntries ->
-                AppListData(
-                    appEntries = appEntries.sortedWith(listModel.getComparator(option)),
-                    option = option,
-                )
-            }
-    }
-
     private fun scheduleOnFirstLoaded() {
-        recordListFlow
+        combinedRecordListFlow
             .waitFirst(appListDataFlow)
             .combine(listModel.flow) { recordList, listModel ->
                 if (listModel.onFirstLoaded(recordList)) {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
index 57a60e5..4a8c00e 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
@@ -32,6 +32,7 @@
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.Dp
 import androidx.lifecycle.viewmodel.compose.viewModel
+import com.android.settingslib.spa.framework.compose.LifecycleEffect
 import com.android.settingslib.spa.framework.compose.LogCompositions
 import com.android.settingslib.spa.framework.compose.TimeMeasurer.Companion.rememberTimeMeasurer
 import com.android.settingslib.spa.framework.compose.rememberLazyListStateAndHideKeyboardWhenStartScroll
@@ -43,7 +44,6 @@
 import com.android.settingslib.spaprivileged.R
 import com.android.settingslib.spaprivileged.framework.compose.DisposableBroadcastReceiverAsUser
 import com.android.settingslib.spaprivileged.model.app.AppEntry
-import com.android.settingslib.spaprivileged.model.app.AppListConfig
 import com.android.settingslib.spaprivileged.model.app.AppListData
 import com.android.settingslib.spaprivileged.model.app.AppListModel
 import com.android.settingslib.spaprivileged.model.app.AppListViewModel
@@ -56,6 +56,14 @@
 private const val TAG = "AppList"
 private const val CONTENT_TYPE_HEADER = "header"
 
+/**
+ * The config used to load the App List.
+ */
+data class AppListConfig(
+    val userIds: List<Int>,
+    val showInstantApps: Boolean,
+)
+
 data class AppListState(
     val showSystem: State<Boolean>,
     val searchQuery: State<String>,
@@ -84,7 +92,7 @@
 internal fun <T : AppRecord> AppListInput<T>.AppListImpl(
     viewModelSupplier: @Composable () -> IAppListViewModel<T>,
 ) {
-    LogCompositions(TAG, config.userId.toString())
+    LogCompositions(TAG, config.userIds.toString())
     val viewModel = viewModelSupplier()
     Column(Modifier.fillMaxSize()) {
         val optionsState = viewModel.spinnerOptionsFlow.collectAsState(null, Dispatchers.IO)
@@ -168,21 +176,23 @@
     listModel: AppListModel<T>,
     state: AppListState,
 ): AppListViewModel<T> {
-    val viewModel: AppListViewModel<T> = viewModel(key = config.userId.toString())
+    val viewModel: AppListViewModel<T> = viewModel(key = config.userIds.toString())
     viewModel.appListConfig.setIfAbsent(config)
     viewModel.listModel.setIfAbsent(listModel)
     viewModel.showSystem.Sync(state.showSystem)
     viewModel.searchQuery.Sync(state.searchQuery)
 
-    DisposableBroadcastReceiverAsUser(
-        intentFilter = IntentFilter(Intent.ACTION_PACKAGE_ADDED).apply {
-            addAction(Intent.ACTION_PACKAGE_REMOVED)
-            addAction(Intent.ACTION_PACKAGE_CHANGED)
-            addDataScheme("package")
-        },
-        userHandle = UserHandle.of(config.userId),
-        onStart = { viewModel.reloadApps() },
-    ) { viewModel.reloadApps() }
-
+    LifecycleEffect(onStart = { viewModel.reloadApps() })
+    val intentFilter = IntentFilter(Intent.ACTION_PACKAGE_ADDED).apply {
+        addAction(Intent.ACTION_PACKAGE_REMOVED)
+        addAction(Intent.ACTION_PACKAGE_CHANGED)
+        addDataScheme("package")
+    }
+    for (userId in config.userIds) {
+        DisposableBroadcastReceiverAsUser(
+            intentFilter = intentFilter,
+            userHandle = UserHandle.of(userId),
+        ) { viewModel.reloadApps() }
+    }
     return viewModel
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
index 404e27c..2ebbe8a 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
@@ -24,10 +24,9 @@
 import com.android.settingslib.spa.widget.scaffold.MoreOptionsScope
 import com.android.settingslib.spa.widget.scaffold.SearchScaffold
 import com.android.settingslib.spaprivileged.R
-import com.android.settingslib.spaprivileged.model.app.AppListConfig
 import com.android.settingslib.spaprivileged.model.app.AppListModel
 import com.android.settingslib.spaprivileged.model.app.AppRecord
-import com.android.settingslib.spaprivileged.template.common.WorkProfilePager
+import com.android.settingslib.spaprivileged.template.common.UserProfilePager
 
 /**
  * The full screen template for an App List page.
@@ -55,10 +54,10 @@
             }
         },
     ) { bottomPadding, searchQuery ->
-        WorkProfilePager(primaryUserOnly) { userInfo ->
+        UserProfilePager(primaryUserOnly) { userGroup ->
             val appListInput = AppListInput(
                 config = AppListConfig(
-                    userId = userInfo.id,
+                    userIds = userGroup.userInfos.map { it.id },
                     showInstantApps = showInstantApps,
                 ),
                 listModel = listModel,
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
new file mode 100644
index 0000000..b5a4929
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.template.common
+
+import android.content.pm.UserInfo
+import android.content.pm.UserProperties
+import android.os.UserHandle
+import android.os.UserManager
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import com.android.settingslib.spa.widget.scaffold.SettingsPager
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import com.android.settingslib.spaprivileged.model.enterprise.EnterpriseRepository
+
+/**
+ * Info about how to group multiple profiles for Settings.
+ *
+ * @see [UserProperties.ShowInSettings]
+ */
+data class UserGroup(
+    /** The users in this user group, if multiple users, the first one is parent user. */
+    val userInfos: List<UserInfo>,
+)
+
+@Composable
+fun UserProfilePager(
+    primaryUserOnly: Boolean = false,
+    content: @Composable (userGroup: UserGroup) -> Unit,
+) {
+    val context = LocalContext.current
+    val userGroups = remember {
+        context.userManager.getUserGroups(primaryUserOnly)
+    }
+    val titles = remember {
+        val enterpriseRepository = EnterpriseRepository(context)
+        userGroups.map { userGroup ->
+            enterpriseRepository.getProfileTitle(
+                isManagedProfile = userGroup.userInfos.first().isManagedProfile,
+            )
+        }
+    }
+
+    SettingsPager(titles) { page ->
+        content(userGroups[page])
+    }
+}
+
+private fun UserManager.getUserGroups(primaryUserOnly: Boolean): List<UserGroup> {
+    val userGroupList = mutableListOf<UserGroup>()
+    val profileToShowInSettingsList = getProfiles(UserHandle.myUserId())
+        .filter { userInfo -> !primaryUserOnly || userInfo.isPrimary }
+        .map { userInfo -> userInfo to getUserProperties(userInfo.userHandle).showInSettings }
+
+    profileToShowInSettingsList.filter { it.second == UserProperties.SHOW_IN_SETTINGS_WITH_PARENT }
+        .takeIf { it.isNotEmpty() }
+        ?.map { it.first }
+        ?.let { userInfos -> userGroupList += UserGroup(userInfos) }
+
+    profileToShowInSettingsList.filter { it.second == UserProperties.SHOW_IN_LAUNCHER_SEPARATE }
+        .forEach { userGroupList += UserGroup(userInfos = listOf(it.first)) }
+
+    return userGroupList
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt
deleted file mode 100644
index a76c438..0000000
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.spaprivileged.template.common
-
-import android.content.pm.UserInfo
-import android.os.UserHandle
-import android.os.UserManager
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.LocalContext
-import com.android.settingslib.spa.widget.scaffold.SettingsPager
-import com.android.settingslib.spaprivileged.model.enterprise.EnterpriseRepository
-
-@Composable
-fun WorkProfilePager(
-    primaryUserOnly: Boolean = false,
-    content: @Composable (userInfo: UserInfo) -> Unit,
-) {
-    val context = LocalContext.current
-    val profiles = remember {
-        val userManager = checkNotNull(context.getSystemService(UserManager::class.java))
-        userManager.getProfiles(UserHandle.myUserId()).filter { userInfo ->
-            !primaryUserOnly || userInfo.isPrimary
-        }
-    }
-    val titles = remember {
-        val enterpriseRepository = EnterpriseRepository(context)
-        profiles.map {
-            enterpriseRepository.getProfileTitle(isManagedProfile = it.isManagedProfile)
-        }
-    }
-
-    SettingsPager(titles) { page ->
-        content(profiles[page])
-    }
-}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt
index 01f4cc6..9bd9242 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt
@@ -85,23 +85,6 @@
         assertThat(onReceiveIsCalled).isTrue()
     }
 
-    @Test
-    fun broadcastReceiver_onStartIsCalled() {
-        var onStartIsCalled = false
-        composeTestRule.setContent {
-            CompositionLocalProvider(LocalContext provides context) {
-                DisposableBroadcastReceiverAsUser(
-                    intentFilter = IntentFilter(),
-                    userHandle = USER_HANDLE,
-                    onStart = { onStartIsCalled = true },
-                    onReceive = {},
-                )
-            }
-        }
-
-        assertThat(onStartIsCalled).isTrue()
-    }
-
     private companion object {
         val USER_HANDLE: UserHandle = UserHandle.of(0)
     }
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
index b0ea40a..57972ed 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
@@ -83,9 +83,8 @@
     @Test
     fun loadApps_notShowInstantApps() = runTest {
         mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP))
-        val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
 
-        val appListFlow = repository.loadApps(appListConfig)
+        val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
 
         assertThat(appListFlow).containsExactly(NORMAL_APP)
     }
@@ -93,9 +92,8 @@
     @Test
     fun loadApps_showInstantApps() = runTest {
         mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP))
-        val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = true)
 
-        val appListFlow = repository.loadApps(appListConfig)
+        val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = true)
 
         assertThat(appListFlow).containsExactly(NORMAL_APP, INSTANT_APP)
     }
@@ -109,9 +107,8 @@
         whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
             .thenReturn(arrayOf(app.packageName))
         mockInstalledApplications(listOf(app))
-        val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
 
-        val appListFlow = repository.loadApps(appListConfig)
+        val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
 
         assertThat(appListFlow).isEmpty()
     }
@@ -126,9 +123,8 @@
         whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
             .thenReturn(arrayOf(app.packageName))
         mockInstalledApplications(listOf(app))
-        val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
 
-        val appListFlow = repository.loadApps(appListConfig)
+        val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
 
         assertThat(appListFlow).isEmpty()
     }
@@ -142,9 +138,8 @@
         whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
             .thenReturn(arrayOf(app.packageName))
         mockInstalledApplications(listOf(app))
-        val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
 
-        val appListFlow = repository.loadApps(appListConfig)
+        val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
 
         assertThat(appListFlow).containsExactly(app)
     }
@@ -157,9 +152,8 @@
             enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
         }
         mockInstalledApplications(listOf(app))
-        val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
 
-        val appListFlow = repository.loadApps(appListConfig)
+        val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
 
         assertThat(appListFlow).containsExactly(app)
     }
@@ -171,9 +165,8 @@
             enabled = false
         }
         mockInstalledApplications(listOf(app))
-        val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
 
-        val appListFlow = repository.loadApps(appListConfig)
+        val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
 
         assertThat(appListFlow).isEmpty()
     }
@@ -223,7 +216,7 @@
 
     @Test
     fun showSystemPredicate_appInLauncher() = runTest {
-        val app = IN_LAUMCHER_APP
+        val app = IN_LAUNCHER_APP
 
         whenever(
             packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), eq(USER_ID))
@@ -237,10 +230,13 @@
     @Test
     fun getSystemPackageNames_returnExpectedValues() = runTest {
         mockInstalledApplications(listOf(
-                NORMAL_APP, INSTANT_APP, SYSTEM_APP, UPDATED_SYSTEM_APP, HOME_APP, IN_LAUMCHER_APP))
-        val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
+                NORMAL_APP, INSTANT_APP, SYSTEM_APP, UPDATED_SYSTEM_APP, HOME_APP, IN_LAUNCHER_APP))
 
-        val systemPackageNames = AppListRepositoryUtil.getSystemPackageNames(context, appListConfig)
+        val systemPackageNames = AppListRepositoryUtil.getSystemPackageNames(
+            context = context,
+            userId = USER_ID,
+            showInstantApps = false,
+        )
 
         assertThat(systemPackageNames).containsExactly("system.app", "home.app", "app.in.launcher")
     }
@@ -280,7 +276,7 @@
             flags = ApplicationInfo.FLAG_SYSTEM
         }
 
-        val IN_LAUMCHER_APP = ApplicationInfo().apply {
+        val IN_LAUNCHER_APP = ApplicationInfo().apply {
             packageName = "app.in.launcher"
             flags = ApplicationInfo.FLAG_SYSTEM
         }
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
index b1d02f5..fc40aed 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
@@ -23,6 +23,7 @@
 import com.android.settingslib.spa.framework.compose.stateOf
 import com.android.settingslib.spa.framework.util.mapItem
 import com.android.settingslib.spa.testutils.waitUntil
+import com.android.settingslib.spaprivileged.template.app.AppListConfig
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -84,14 +85,17 @@
     }
 
     private object FakeAppListRepository : AppListRepository {
-        override suspend fun loadApps(config: AppListConfig) = listOf(APP)
+        override suspend fun loadApps(userId: Int, showInstantApps: Boolean) = listOf(APP)
 
         override fun showSystemPredicate(
             userIdFlow: Flow<Int>,
             showSystemFlow: Flow<Boolean>,
         ): Flow<(app: ApplicationInfo) -> Boolean> = flowOf { true }
 
-        override fun getSystemPackageNamesBlocking(config: AppListConfig): Set<String> = setOf()
+        override fun getSystemPackageNamesBlocking(
+            userId: Int,
+            showInstantApps: Boolean,
+        ): Set<String> = emptySet()
     }
 
     private object FakeAppRepository : AppRepository {
@@ -105,7 +109,7 @@
         const val USER_ID = 0
         const val PACKAGE_NAME = "package.name"
         const val LABEL = "Label"
-        val CONFIG = AppListConfig(userId = USER_ID, showInstantApps = false)
+        val CONFIG = AppListConfig(userIds = listOf(USER_ID), showInstantApps = false)
         val APP = ApplicationInfo().apply {
             packageName = PACKAGE_NAME
         }
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt
index d5c7c19..a99d02d 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt
@@ -32,7 +32,6 @@
 import com.android.settingslib.spa.widget.ui.SpinnerOption
 import com.android.settingslib.spaprivileged.R
 import com.android.settingslib.spaprivileged.model.app.AppEntry
-import com.android.settingslib.spaprivileged.model.app.AppListConfig
 import com.android.settingslib.spaprivileged.model.app.AppListData
 import com.android.settingslib.spaprivileged.model.app.IAppListViewModel
 import com.android.settingslib.spaprivileged.tests.testutils.TestAppListModel
@@ -115,7 +114,7 @@
     ) {
         composeTestRule.setContent {
             AppListInput(
-                config = AppListConfig(userId = USER_ID, showInstantApps = false),
+                config = AppListConfig(userIds = listOf(USER_ID), showInstantApps = false),
                 listModel = TestAppListModel(enableGrouping = enableGrouping),
                 state = AppListState(
                     showSystem = false.toState(),
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
index db224be..a82f070 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -30,6 +30,8 @@
     public static final String CATEGORY_APPS = "com.android.settings.category.ia.apps";
     public static final String CATEGORY_APPS_DEFAULT =
             "com.android.settings.category.ia.apps.default";
+    public static final String CATEGORY_SPECIAL_APP_ACCESS =
+            "com.android.settings.category.ia.special_app_access";
     public static final String CATEGORY_BATTERY = "com.android.settings.category.ia.battery";
     public static final String CATEGORY_DISPLAY = "com.android.settings.category.ia.display";
     public static final String CATEGORY_SOUND = "com.android.settings.category.ia.sound";
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index ede69be..75d28d5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -228,9 +228,6 @@
                 Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
                 GlobalSettingsProto.App.AUTO_RESTRICTION_ENABLED);
         dumpSetting(s, p,
-                Settings.Global.FORCED_APP_STANDBY_ENABLED,
-                GlobalSettingsProto.App.FORCED_APP_STANDBY_ENABLED);
-        dumpSetting(s, p,
                 Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
                 GlobalSettingsProto.App.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED);
         p.end(appToken);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index db7032e..01740319 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -285,7 +285,6 @@
                     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,
                     Settings.Global.WIFI_ON_WHEN_PROXY_DISCONNECTED,
                     Settings.Global.FSTRIM_MANDATORY_INTERVAL,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 697e181..f305981 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -112,6 +112,24 @@
     ],
 }
 
+//Create a library to expose SystemUI's resources to other modules.
+android_library {
+    name: "SystemUI-res",
+    resource_dirs: [
+        "res-product",
+        "res-keyguard",
+        "res",
+    ],
+    static_libs: [
+        "SystemUISharedLib",
+        "SettingsLib",
+        "androidx.leanback_leanback",
+        "androidx.slice_slice-core",
+        "androidx.slice_slice-view",
+    ],
+    manifest: "AndroidManifest-res.xml",
+}
+
 android_library {
     name: "SystemUI-core",
     defaults: [
diff --git a/packages/SystemUI/AndroidManifest-res.xml b/packages/SystemUI/AndroidManifest-res.xml
new file mode 100644
index 0000000..58e9dc6
--- /dev/null
+++ b/packages/SystemUI/AndroidManifest-res.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<manifest package= "com.android.systemui.res">
+    <application/>
+</manifest>
\ No newline at end of file
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 8f70dcc..c729b09 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -301,10 +301,13 @@
 
     interface Callback {
         /** Whether we are currently on the keyguard or not. */
-        fun isOnKeyguard(): Boolean
+        @JvmDefault fun isOnKeyguard(): Boolean = false
 
         /** Hide the keyguard and animate using [runner]. */
-        fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner)
+        @JvmDefault
+        fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner) {
+            throw UnsupportedOperationException()
+        }
 
         /* Get the background color of [task]. */
         fun getBackgroundColor(task: TaskInfo): Int
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
index 3d341af..2903288 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
@@ -33,9 +33,7 @@
 private const val FONT_ITALIC_ANIMATION_STEP = 0.1f
 private const val FONT_ITALIC_DEFAULT_VALUE = 0f
 
-/**
- * Provide interpolation of two fonts by adjusting font variation settings.
- */
+/** Provide interpolation of two fonts by adjusting font variation settings. */
 class FontInterpolator {
 
     /**
@@ -61,11 +59,14 @@
         var index: Int,
         val sortedAxes: MutableList<FontVariationAxis>
     ) {
-        constructor(font: Font, axes: List<FontVariationAxis>) :
-                this(font.sourceIdentifier,
-                        font.ttcIndex,
-                        axes.toMutableList().apply { sortBy { it.tag } }
-                )
+        constructor(
+            font: Font,
+            axes: List<FontVariationAxis>
+        ) : this(
+            font.sourceIdentifier,
+            font.ttcIndex,
+            axes.toMutableList().apply { sortBy { it.tag } }
+        )
 
         fun set(font: Font, axes: List<FontVariationAxis>) {
             sourceId = font.sourceIdentifier
@@ -86,9 +87,7 @@
     private val tmpInterpKey = InterpKey(null, null, 0f)
     private val tmpVarFontKey = VarFontKey(0, 0, mutableListOf())
 
-    /**
-     * Linear interpolate the font variation settings.
-     */
+    /** Linear interpolate the font variation settings. */
     fun lerp(start: Font, end: Font, progress: Float): Font {
         if (progress == 0f) {
             return start
@@ -115,27 +114,34 @@
         // this doesn't take much time since the variation axes is usually up to 5. If we need to
         // support more number of axes, we may want to preprocess the font and store the sorted axes
         // and also pre-fill the missing axes value with default value from 'fvar' table.
-        val newAxes = lerp(startAxes, endAxes) { tag, startValue, endValue ->
-            when (tag) {
-                // TODO: Good to parse 'fvar' table for retrieving default value.
-                TAG_WGHT -> adjustWeight(
-                        MathUtils.lerp(
+        val newAxes =
+            lerp(startAxes, endAxes) { tag, startValue, endValue ->
+                when (tag) {
+                    // TODO: Good to parse 'fvar' table for retrieving default value.
+                    TAG_WGHT ->
+                        adjustWeight(
+                            MathUtils.lerp(
                                 startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
                                 endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
-                                progress))
-                TAG_ITAL -> adjustItalic(
-                        MathUtils.lerp(
+                                progress
+                            )
+                        )
+                    TAG_ITAL ->
+                        adjustItalic(
+                            MathUtils.lerp(
                                 startValue ?: FONT_ITALIC_DEFAULT_VALUE,
                                 endValue ?: FONT_ITALIC_DEFAULT_VALUE,
-                                progress))
-                else -> {
-                    require(startValue != null && endValue != null) {
-                        "Unable to interpolate due to unknown default axes value : $tag"
+                                progress
+                            )
+                        )
+                    else -> {
+                        require(startValue != null && endValue != null) {
+                            "Unable to interpolate due to unknown default axes value : $tag"
+                        }
+                        MathUtils.lerp(startValue, endValue, progress)
                     }
-                    MathUtils.lerp(startValue, endValue, progress)
                 }
             }
-        }
 
         // Check if we already make font for this axes. This is typically happens if the animation
         // happens backward.
@@ -149,9 +155,7 @@
         // This is the first time to make the font for the axes. Build and store it to the cache.
         // Font.Builder#build won't throw IOException since creating fonts from existing fonts will
         // not do any IO work.
-        val newFont = Font.Builder(start)
-                .setFontVariationSettings(newAxes.toTypedArray())
-                .build()
+        val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build()
         interpCache[InterpKey(start, end, progress)] = newFont
         verFontCache[VarFontKey(start, newAxes)] = newFont
         return newFont
@@ -173,26 +177,28 @@
             val tagA = if (i < start.size) start[i].tag else null
             val tagB = if (j < end.size) end[j].tag else null
 
-            val comp = when {
-                tagA == null -> 1
-                tagB == null -> -1
-                else -> tagA.compareTo(tagB)
-            }
+            val comp =
+                when {
+                    tagA == null -> 1
+                    tagB == null -> -1
+                    else -> tagA.compareTo(tagB)
+                }
 
-            val axis = when {
-                comp == 0 -> {
-                    val v = filter(tagA!!, start[i++].styleValue, end[j++].styleValue)
-                    FontVariationAxis(tagA, v)
+            val axis =
+                when {
+                    comp == 0 -> {
+                        val v = filter(tagA!!, start[i++].styleValue, end[j++].styleValue)
+                        FontVariationAxis(tagA, v)
+                    }
+                    comp < 0 -> {
+                        val v = filter(tagA!!, start[i++].styleValue, null)
+                        FontVariationAxis(tagA, v)
+                    }
+                    else -> { // comp > 0
+                        val v = filter(tagB!!, null, end[j++].styleValue)
+                        FontVariationAxis(tagB, v)
+                    }
                 }
-                comp < 0 -> {
-                    val v = filter(tagA!!, start[i++].styleValue, null)
-                    FontVariationAxis(tagA, v)
-                }
-                else -> { // comp > 0
-                    val v = filter(tagB!!, null, end[j++].styleValue)
-                    FontVariationAxis(tagB, v)
-                }
-            }
 
             result.add(axis)
         }
@@ -202,21 +208,21 @@
     // For the performance reasons, we animate weight with FONT_WEIGHT_ANIMATION_STEP. This helps
     // Cache hit ratio in the Skia glyph cache.
     private fun adjustWeight(value: Float) =
-            coerceInWithStep(value, FONT_WEIGHT_MIN, FONT_WEIGHT_MAX, FONT_WEIGHT_ANIMATION_STEP)
+        coerceInWithStep(value, FONT_WEIGHT_MIN, FONT_WEIGHT_MAX, FONT_WEIGHT_ANIMATION_STEP)
 
     // For the performance reasons, we animate italic with FONT_ITALIC_ANIMATION_STEP. This helps
     // Cache hit ratio in the Skia glyph cache.
     private fun adjustItalic(value: Float) =
-            coerceInWithStep(value, FONT_ITALIC_MIN, FONT_ITALIC_MAX, FONT_ITALIC_ANIMATION_STEP)
+        coerceInWithStep(value, FONT_ITALIC_MIN, FONT_ITALIC_MAX, FONT_ITALIC_ANIMATION_STEP)
 
     private fun coerceInWithStep(v: Float, min: Float, max: Float, step: Float) =
-            (v.coerceIn(min, max) / step).toInt() * step
+        (v.coerceIn(min, max) / step).toInt() * step
 
     companion object {
         private val EMPTY_AXES = arrayOf<FontVariationAxis>()
 
         // Returns true if given two font instance can be interpolated.
         fun canInterpolate(start: Font, end: Font) =
-                start.ttcIndex == end.ttcIndex && start.sourceIdentifier == end.sourceIdentifier
+            start.ttcIndex == end.ttcIndex && start.sourceIdentifier == end.sourceIdentifier
     }
 }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index 5f1bb83..fdab749 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -36,8 +36,8 @@
  * Currently this class can provide text style animation for text weight and text size. For example
  * the simple view that draws text with animating text size is like as follows:
  *
- * <pre>
- * <code>
+ * <pre> <code>
+ * ```
  *     class SimpleTextAnimation : View {
  *         @JvmOverloads constructor(...)
  *
@@ -53,83 +53,63 @@
  *             animator.setTextStyle(-1 /* unchanged weight */, sizePx, animate)
  *         }
  *     }
- * </code>
- * </pre>
+ * ```
+ * </code> </pre>
  */
-class TextAnimator(
-    layout: Layout,
-    private val invalidateCallback: () -> Unit
-) {
+class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
     // Following two members are for mutable for testing purposes.
     public var textInterpolator: TextInterpolator = TextInterpolator(layout)
-    public var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply {
-        duration = DEFAULT_ANIMATION_DURATION
-        addUpdateListener {
-            textInterpolator.progress = it.animatedValue as Float
-            invalidateCallback()
-        }
-        addListener(object : AnimatorListenerAdapter() {
-            override fun onAnimationEnd(animation: Animator?) {
-                textInterpolator.rebase()
+    public var animator: ValueAnimator =
+        ValueAnimator.ofFloat(1f).apply {
+            duration = DEFAULT_ANIMATION_DURATION
+            addUpdateListener {
+                textInterpolator.progress = it.animatedValue as Float
+                invalidateCallback()
             }
-            override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase()
-        })
-    }
+            addListener(
+                object : AnimatorListenerAdapter() {
+                    override fun onAnimationEnd(animation: Animator?) {
+                        textInterpolator.rebase()
+                    }
+                    override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase()
+                }
+            )
+        }
 
     sealed class PositionedGlyph {
 
-        /**
-         * Mutable X coordinate of the glyph position relative from drawing offset.
-         */
+        /** Mutable X coordinate of the glyph position relative from drawing offset. */
         var x: Float = 0f
 
-        /**
-         * Mutable Y coordinate of the glyph position relative from the baseline.
-         */
+        /** Mutable Y coordinate of the glyph position relative from the baseline. */
         var y: Float = 0f
 
-        /**
-         * The current line of text being drawn, in a multi-line TextView.
-         */
+        /** The current line of text being drawn, in a multi-line TextView. */
         var lineNo: Int = 0
 
-        /**
-         * Mutable text size of the glyph in pixels.
-         */
+        /** Mutable text size of the glyph in pixels. */
         var textSize: Float = 0f
 
-        /**
-         * Mutable color of the glyph.
-         */
+        /** Mutable color of the glyph. */
         var color: Int = 0
 
-        /**
-         * Immutable character offset in the text that the current font run start.
-         */
+        /** Immutable character offset in the text that the current font run start. */
         abstract var runStart: Int
             protected set
 
-        /**
-         * Immutable run length of the font run.
-         */
+        /** Immutable run length of the font run. */
         abstract var runLength: Int
             protected set
 
-        /**
-         * Immutable glyph index of the font run.
-         */
+        /** Immutable glyph index of the font run. */
         abstract var glyphIndex: Int
             protected set
 
-        /**
-         * Immutable font instance for this font run.
-         */
+        /** Immutable font instance for this font run. */
         abstract var font: Font
             protected set
 
-        /**
-         * Immutable glyph ID for this glyph.
-         */
+        /** Immutable glyph ID for this glyph. */
         abstract var glyphId: Int
             protected set
     }
@@ -147,20 +127,18 @@
     /**
      * GlyphFilter applied just before drawing to canvas for tweaking positions and text size.
      *
-     * This callback is called for each glyphs just before drawing the glyphs. This function will
-     * be called with the intrinsic position, size, color, glyph ID and font instance. You can
-     * mutate the position, size and color for tweaking animations.
-     * Do not keep the reference of passed glyph object. The interpolator reuses that object for
-     * avoiding object allocations.
+     * This callback is called for each glyphs just before drawing the glyphs. This function will be
+     * called with the intrinsic position, size, color, glyph ID and font instance. You can mutate
+     * the position, size and color for tweaking animations. Do not keep the reference of passed
+     * glyph object. The interpolator reuses that object for avoiding object allocations.
      *
-     * Details:
-     * The text is drawn with font run units. The font run is a text segment that draws with the
-     * same font. The {@code runStart} and {@code runLimit} is a range of the font run in the text
-     * that current glyph is in. Once the font run is determined, the system will convert characters
-     * into glyph IDs. The {@code glyphId} is the glyph identifier in the font and
-     * {@code glyphIndex} is the offset of the converted glyph array. Please note that the
-     * {@code glyphIndex} is not a character index, because the character will not be converted to
-     * glyph one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be
+     * Details: The text is drawn with font run units. The font run is a text segment that draws
+     * with the same font. The {@code runStart} and {@code runLimit} is a range of the font run in
+     * the text that current glyph is in. Once the font run is determined, the system will convert
+     * characters into glyph IDs. The {@code glyphId} is the glyph identifier in the font and {@code
+     * glyphIndex} is the offset of the converted glyph array. Please note that the {@code
+     * glyphIndex} is not a character index, because the character will not be converted to glyph
+     * one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be
      * composed from multiple characters.
      *
      * Here is an example of font runs: "fin. 終わり"
@@ -193,7 +171,9 @@
      */
     var glyphFilter: GlyphCallback?
         get() = textInterpolator.glyphFilter
-        set(value) { textInterpolator.glyphFilter = value }
+        set(value) {
+            textInterpolator.glyphFilter = value
+        }
 
     fun draw(c: Canvas) = textInterpolator.draw(c)
 
@@ -208,7 +188,7 @@
      * @param weight an optional text weight.
      * @param textSize an optional font size.
      * @param colors an optional colors array that must be the same size as numLines passed to
-     *  the TextInterpolator
+     *               the TextInterpolator
      * @param animate an optional boolean indicating true for showing style transition as animation,
      *                false for immediate style transition. True by default.
      * @param duration an optional animation duration in milliseconds. This is ignored if animate is
@@ -237,10 +217,11 @@
         if (weight >= 0) {
             // Paint#setFontVariationSettings creates Typeface instance from scratch. To reduce the
             // memory impact, cache the typeface result.
-            textInterpolator.targetPaint.typeface = typefaceCache.getOrElse(weight) {
-                textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
-                textInterpolator.targetPaint.typeface
-            }
+            textInterpolator.targetPaint.typeface =
+                typefaceCache.getOrElse(weight) {
+                    textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
+                    textInterpolator.targetPaint.typeface
+                }
         }
         if (color != null) {
             textInterpolator.targetPaint.color = color
@@ -249,22 +230,24 @@
 
         if (animate) {
             animator.startDelay = delay
-            animator.duration = if (duration == -1L) {
-                DEFAULT_ANIMATION_DURATION
-            } else {
-                duration
-            }
+            animator.duration =
+                if (duration == -1L) {
+                    DEFAULT_ANIMATION_DURATION
+                } else {
+                    duration
+                }
             interpolator?.let { animator.interpolator = it }
             if (onAnimationEnd != null) {
-                val listener = object : AnimatorListenerAdapter() {
-                    override fun onAnimationEnd(animation: Animator?) {
-                        onAnimationEnd.run()
-                        animator.removeListener(this)
+                val listener =
+                    object : AnimatorListenerAdapter() {
+                        override fun onAnimationEnd(animation: Animator?) {
+                            onAnimationEnd.run()
+                            animator.removeListener(this)
+                        }
+                        override fun onAnimationCancel(animation: Animator?) {
+                            animator.removeListener(this)
+                        }
                     }
-                    override fun onAnimationCancel(animation: Animator?) {
-                        animator.removeListener(this)
-                    }
-                }
                 animator.addListener(listener)
             }
             animator.start()
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
index 0448c81..341784e 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
@@ -26,12 +26,8 @@
 import com.android.internal.graphics.ColorUtils
 import java.lang.Math.max
 
-/**
- * Provide text style linear interpolation for plain text.
- */
-class TextInterpolator(
-    layout: Layout
-) {
+/** Provide text style linear interpolation for plain text. */
+class TextInterpolator(layout: Layout) {
 
     /**
      * Returns base paint used for interpolation.
@@ -64,12 +60,11 @@
         var baseFont: Font,
         var targetFont: Font
     ) {
-        val length: Int get() = end - start
+        val length: Int
+            get() = end - start
     }
 
-    /**
-     * A class represents text layout of a single run.
-     */
+    /** A class represents text layout of a single run. */
     private class Run(
         val glyphIds: IntArray,
         val baseX: FloatArray, // same length as glyphIds
@@ -79,12 +74,8 @@
         val fontRuns: List<FontRun>
     )
 
-    /**
-     * A class represents text layout of a single line.
-     */
-    private class Line(
-        val runs: List<Run>
-    )
+    /** A class represents text layout of a single line. */
+    private class Line(val runs: List<Run>)
 
     private var lines = listOf<Line>()
     private val fontInterpolator = FontInterpolator()
@@ -106,8 +97,8 @@
     /**
      * The layout used for drawing text.
      *
-     * Only non-styled text is supported. Even if the given layout is created from Spanned, the
-     * span information is not used.
+     * Only non-styled text is supported. Even if the given layout is created from Spanned, the span
+     * information is not used.
      *
      * The paint objects used for interpolation are not changed by this method call.
      *
@@ -122,6 +113,9 @@
             shapeText(value)
         }
 
+    var shapedText: String = ""
+        private set
+
     init {
         // shapeText needs to be called after all members are initialized.
         shapeText(layout)
@@ -130,8 +124,8 @@
     /**
      * Recalculate internal text layout for interpolation.
      *
-     * Whenever the target paint is modified, call this method to recalculate internal
-     * text layout used for interpolation.
+     * Whenever the target paint is modified, call this method to recalculate internal text layout
+     * used for interpolation.
      */
     fun onTargetPaintModified() {
         updatePositionsAndFonts(shapeText(layout, targetPaint), updateBase = false)
@@ -140,8 +134,8 @@
     /**
      * Recalculate internal text layout for interpolation.
      *
-     * Whenever the base paint is modified, call this method to recalculate internal
-     * text layout used for interpolation.
+     * Whenever the base paint is modified, call this method to recalculate internal text layout
+     * used for interpolation.
      */
     fun onBasePaintModified() {
         updatePositionsAndFonts(shapeText(layout, basePaint), updateBase = true)
@@ -152,11 +146,11 @@
      *
      * The text interpolator does not calculate all the text position by text shaper due to
      * performance reasons. Instead, the text interpolator shape the start and end state and
-     * calculate text position of the middle state by linear interpolation. Due to this trick,
-     * the text positions of the middle state is likely different from the text shaper result.
-     * So, if you want to start animation from the middle state, you will see the glyph jumps due to
-     * this trick, i.e. the progress 0.5 of interpolation between weight 400 and 700 is different
-     * from text shape result of weight 550.
+     * calculate text position of the middle state by linear interpolation. Due to this trick, the
+     * text positions of the middle state is likely different from the text shaper result. So, if
+     * you want to start animation from the middle state, you will see the glyph jumps due to this
+     * trick, i.e. the progress 0.5 of interpolation between weight 400 and 700 is different from
+     * text shape result of weight 550.
      *
      * After calling this method, do not call onBasePaintModified() since it reshape the text and
      * update the base state. As in above notice, the text shaping result at current progress is
@@ -168,8 +162,8 @@
      * animate weight from 200 to 400, then if you want to move back to 200 at the half of the
      * animation, it will look like
      *
-     * <pre>
-     * <code>
+     * <pre> <code>
+     * ```
      *     val interp = TextInterpolator(layout)
      *
      *     // Interpolate between weight 200 to 400.
@@ -199,9 +193,8 @@
      *         // progress is 0.5
      *         animator.start()
      *     }
-     * </code>
-     * </pre>
-     *
+     * ```
+     * </code> </pre>
      */
     fun rebase() {
         if (progress == 0f) {
@@ -263,69 +256,73 @@
         }
 
         var maxRunLength = 0
-        lines = baseLayout.zip(targetLayout) { baseLine, targetLine ->
-            val runs = baseLine.zip(targetLine) { base, target ->
-
-                require(base.glyphCount() == target.glyphCount()) {
-                    "Inconsistent glyph count at line ${lines.size}"
-                }
-
-                val glyphCount = base.glyphCount()
-
-                // Good to recycle the array if the existing array can hold the new layout result.
-                val glyphIds = IntArray(glyphCount) {
-                    base.getGlyphId(it).also { baseGlyphId ->
-                        require(baseGlyphId == target.getGlyphId(it)) {
-                            "Inconsistent glyph ID at $it in line ${lines.size}"
+        lines =
+            baseLayout.zip(targetLayout) { baseLine, targetLine ->
+                val runs =
+                    baseLine.zip(targetLine) { base, target ->
+                        require(base.glyphCount() == target.glyphCount()) {
+                            "Inconsistent glyph count at line ${lines.size}"
                         }
-                    }
-                }
 
-                val baseX = FloatArray(glyphCount) { base.getGlyphX(it) }
-                val baseY = FloatArray(glyphCount) { base.getGlyphY(it) }
-                val targetX = FloatArray(glyphCount) { target.getGlyphX(it) }
-                val targetY = FloatArray(glyphCount) { target.getGlyphY(it) }
+                        val glyphCount = base.glyphCount()
 
-                // Calculate font runs
-                val fontRun = mutableListOf<FontRun>()
-                if (glyphCount != 0) {
-                    var start = 0
-                    var baseFont = base.getFont(start)
-                    var targetFont = target.getFont(start)
-                    require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
-                        "Cannot interpolate font at $start ($baseFont vs $targetFont)"
-                    }
-
-                    for (i in 1 until glyphCount) {
-                        val nextBaseFont = base.getFont(i)
-                        val nextTargetFont = target.getFont(i)
-
-                        if (baseFont !== nextBaseFont) {
-                            require(targetFont !== nextTargetFont) {
-                                "Base font has changed at $i but target font has not changed."
+                        // Good to recycle the array if the existing array can hold the new layout
+                        // result.
+                        val glyphIds =
+                            IntArray(glyphCount) {
+                                base.getGlyphId(it).also { baseGlyphId ->
+                                    require(baseGlyphId == target.getGlyphId(it)) {
+                                        "Inconsistent glyph ID at $it in line ${lines.size}"
+                                    }
+                                }
                             }
-                            // Font transition point. push run and reset context.
-                            fontRun.add(FontRun(start, i, baseFont, targetFont))
-                            maxRunLength = max(maxRunLength, i - start)
-                            baseFont = nextBaseFont
-                            targetFont = nextTargetFont
-                            start = i
+
+                        val baseX = FloatArray(glyphCount) { base.getGlyphX(it) }
+                        val baseY = FloatArray(glyphCount) { base.getGlyphY(it) }
+                        val targetX = FloatArray(glyphCount) { target.getGlyphX(it) }
+                        val targetY = FloatArray(glyphCount) { target.getGlyphY(it) }
+
+                        // Calculate font runs
+                        val fontRun = mutableListOf<FontRun>()
+                        if (glyphCount != 0) {
+                            var start = 0
+                            var baseFont = base.getFont(start)
+                            var targetFont = target.getFont(start)
                             require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
                                 "Cannot interpolate font at $start ($baseFont vs $targetFont)"
                             }
-                        } else { // baseFont === nextBaseFont
-                            require(targetFont === nextTargetFont) {
-                                "Base font has not changed at $i but target font has changed."
+
+                            for (i in 1 until glyphCount) {
+                                val nextBaseFont = base.getFont(i)
+                                val nextTargetFont = target.getFont(i)
+
+                                if (baseFont !== nextBaseFont) {
+                                    require(targetFont !== nextTargetFont) {
+                                        "Base font has changed at $i but target font is unchanged."
+                                    }
+                                    // Font transition point. push run and reset context.
+                                    fontRun.add(FontRun(start, i, baseFont, targetFont))
+                                    maxRunLength = max(maxRunLength, i - start)
+                                    baseFont = nextBaseFont
+                                    targetFont = nextTargetFont
+                                    start = i
+                                    require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
+                                        "Cannot interpolate font at $start" +
+                                            " ($baseFont vs $targetFont)"
+                                    }
+                                } else { // baseFont === nextBaseFont
+                                    require(targetFont === nextTargetFont) {
+                                        "Base font is unchanged at $i but target font has changed."
+                                    }
+                                }
                             }
+                            fontRun.add(FontRun(start, glyphCount, baseFont, targetFont))
+                            maxRunLength = max(maxRunLength, glyphCount - start)
                         }
+                        Run(glyphIds, baseX, baseY, targetX, targetY, fontRun)
                     }
-                    fontRun.add(FontRun(start, glyphCount, baseFont, targetFont))
-                    maxRunLength = max(maxRunLength, glyphCount - start)
-                }
-                Run(glyphIds, baseX, baseY, targetX, targetY, fontRun)
+                Line(runs)
             }
-            Line(runs)
-        }
 
         // Update float array used for drawing.
         if (tmpPositionArray.size < maxRunLength * 2) {
@@ -357,9 +354,9 @@
         if (glyphFilter == null) {
             for (i in run.start until run.end) {
                 tmpPositionArray[arrayIndex++] =
-                        MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
+                    MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
                 tmpPositionArray[arrayIndex++] =
-                        MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
+                    MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
             }
             c.drawGlyphs(line.glyphIds, run.start, tmpPositionArray, 0, run.length, font, paint)
             return
@@ -388,13 +385,14 @@
                 tmpPaintForGlyph.color = tmpGlyph.color
 
                 c.drawGlyphs(
-                        line.glyphIds,
-                        prevStart,
-                        tmpPositionArray,
-                        0,
-                        i - prevStart,
-                        font,
-                        tmpPaintForGlyph)
+                    line.glyphIds,
+                    prevStart,
+                    tmpPositionArray,
+                    0,
+                    i - prevStart,
+                    font,
+                    tmpPaintForGlyph
+                )
                 prevStart = i
                 arrayIndex = 0
             }
@@ -404,13 +402,14 @@
         }
 
         c.drawGlyphs(
-                line.glyphIds,
-                prevStart,
-                tmpPositionArray,
-                0,
-                run.end - prevStart,
-                font,
-                tmpPaintForGlyph)
+            line.glyphIds,
+            prevStart,
+            tmpPositionArray,
+            0,
+            run.end - prevStart,
+            font,
+            tmpPaintForGlyph
+        )
     }
 
     private fun updatePositionsAndFonts(
@@ -418,9 +417,7 @@
         updateBase: Boolean
     ) {
         // Update target positions with newly calculated text layout.
-        check(layoutResult.size == lines.size) {
-            "The new layout result has different line count."
-        }
+        check(layoutResult.size == lines.size) { "The new layout result has different line count." }
 
         lines.zip(layoutResult) { line, runs ->
             line.runs.zip(runs) { lineRun, newGlyphs ->
@@ -436,7 +433,7 @@
                         }
                         require(newFont === newGlyphs.getFont(i)) {
                             "The new layout has different font run." +
-                                    " $newFont vs ${newGlyphs.getFont(i)} at $i"
+                                " $newFont vs ${newGlyphs.getFont(i)} at $i"
                         }
                     }
 
@@ -444,7 +441,7 @@
                     // check new font can be interpolatable with base font.
                     require(FontInterpolator.canInterpolate(newFont, run.baseFont)) {
                         "New font cannot be interpolated with existing font. $newFont," +
-                                " ${run.baseFont}"
+                            " ${run.baseFont}"
                     }
 
                     if (updateBase) {
@@ -480,14 +477,13 @@
     }
 
     // Shape the text and stores the result to out argument.
-    private fun shapeText(
-        layout: Layout,
-        paint: TextPaint
-    ): List<List<PositionedGlyphs>> {
+    private fun shapeText(layout: Layout, paint: TextPaint): List<List<PositionedGlyphs>> {
+        var text = StringBuilder()
         val out = mutableListOf<List<PositionedGlyphs>>()
         for (lineNo in 0 until layout.lineCount) { // Shape all lines.
             val lineStart = layout.getLineStart(lineNo)
-            var count = layout.getLineEnd(lineNo) - lineStart
+            val lineEnd = layout.getLineEnd(lineNo)
+            var count = lineEnd - lineStart
             // Do not render the last character in the line if it's a newline and unprintable
             val last = lineStart + count - 1
             if (last > lineStart && last < layout.text.length && layout.text[last] == '\n') {
@@ -495,19 +491,28 @@
             }
 
             val runs = mutableListOf<PositionedGlyphs>()
-            TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic,
-                    paint) { _, _, glyphs, _ ->
-                runs.add(glyphs)
-            }
+            TextShaper.shapeText(
+                layout.text,
+                lineStart,
+                count,
+                layout.textDirectionHeuristic,
+                paint
+            ) { _, _, glyphs, _ -> runs.add(glyphs) }
             out.add(runs)
+
+            if (lineNo > 0) {
+                text.append("\n")
+            }
+            text.append(layout.text.substring(lineStart, lineEnd))
         }
+        shapedText = text.toString()
         return out
     }
 }
 
 private fun Layout.getDrawOrigin(lineNo: Int) =
-        if (getParagraphDirection(lineNo) == Layout.DIR_LEFT_TO_RIGHT) {
-            getLineLeft(lineNo)
-        } else {
-            getLineRight(lineNo)
-        }
+    if (getParagraphDirection(lineNo) == Layout.DIR_LEFT_TO_RIGHT) {
+        getLineLeft(lineNo)
+    } else {
+        getLineRight(lineNo)
+    }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index 7f1c78f..e4e9c46 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -178,6 +178,9 @@
         /** Flag denoting whether the customizable clocks feature is enabled. */
         const val FLAG_NAME_CUSTOM_CLOCKS_ENABLED = "is_custom_clocks_feature_enabled"
 
+        /** Flag denoting whether the Wallpaper preview should use the full screen UI. */
+        const val FLAG_NAME_WALLPAPER_FULLSCREEN_PREVIEW = "wallpaper_fullscreen_preview"
+
         object Columns {
             /** String. Unique ID for the flag. */
             const val NAME = "name"
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 6d970b3..3d3ccf4 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -1,7 +1,5 @@
 +packages/SystemUI
--packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
 -packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
--packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
 -packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
 -packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt
 -packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
index dfb73a9..87b5a4c 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
@@ -16,58 +16,13 @@
 * limitations under the License.
 */
 -->
-<selector
+<shape
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
-
-  <item android:state_selected="true">
-    <layer-list>
-      <item
-          android:left="3dp"
-          android:top="3dp"
-          android:right="3dp"
-          android:bottom="3dp">
-        <!-- We make the shapes a rounded rectangle instead of an oval so that it can animate -->
-        <!-- properly into an app/dialog. -->
-        <shape android:shape="rectangle">
-          <solid android:color="?androidprv:attr/colorSurface"/>
-          <size
-              android:width="@dimen/keyguard_affordance_fixed_width"
-              android:height="@dimen/keyguard_affordance_fixed_height"/>
-          <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
-        </shape>
-      </item>
-
-      <item>
-        <shape android:shape="rectangle">
-          <stroke
-              android:color="@color/control_primary_text"
-              android:width="2dp"/>
-          <size
-              android:width="@dimen/keyguard_affordance_fixed_width"
-              android:height="@dimen/keyguard_affordance_fixed_height"/>
-          <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
-        </shape>
-      </item>
-    </layer-list>
-  </item>
-
-  <item>
-    <layer-list>
-      <item
-          android:left="3dp"
-          android:top="3dp"
-          android:right="3dp"
-          android:bottom="3dp">
-        <shape android:shape="rectangle">
-          <solid android:color="?androidprv:attr/colorSurface"/>
-          <size
-              android:width="@dimen/keyguard_affordance_fixed_width"
-              android:height="@dimen/keyguard_affordance_fixed_height"/>
-          <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
-        </shape>
-      </item>
-    </layer-list>
-  </item>
-
-</selector>
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+  <solid android:color="?androidprv:attr/colorSurface"/>
+  <size
+      android:width="@dimen/keyguard_affordance_fixed_width"
+      android:height="@dimen/keyguard_affordance_fixed_height"/>
+  <corners android:radius="@dimen/keyguard_affordance_fixed_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
new file mode 100644
index 0000000..acd2462
--- /dev/null
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright 2023, The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+  <item android:state_selected="true">
+    <shape android:shape="oval">
+      <stroke
+          android:color="@color/control_primary_text"
+          android:width="2dp"/>
+      <size
+          android:width="@dimen/keyguard_affordance_fixed_width"
+          android:height="@dimen/keyguard_affordance_fixed_height"/>
+    </shape>
+  </item>
+</selector>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 6120863..3f95515 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -67,6 +67,7 @@
         android:scaleType="center"
         android:tint="?android:attr/textColorPrimary"
         android:background="@drawable/keyguard_bottom_affordance_bg"
+        android:foreground="@drawable/keyguard_bottom_affordance_selected_border"
         android:layout_marginStart="@dimen/keyguard_affordance_horizontal_offset"
         android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
         android:visibility="gone" />
@@ -79,6 +80,7 @@
         android:scaleType="center"
         android:tint="?android:attr/textColorPrimary"
         android:background="@drawable/keyguard_bottom_affordance_bg"
+        android:foreground="@drawable/keyguard_bottom_affordance_selected_border"
         android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset"
         android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
         android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index b76de5a..e182a6a 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -24,6 +24,7 @@
     android:orientation="vertical">
 
     <LinearLayout
+        android:id="@+id/media_metadata_section"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="start|center_vertical"
diff --git a/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml b/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
index 21d12c2..4483db8 100644
--- a/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
+++ b/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
@@ -27,6 +27,14 @@
         android:layout_height="wrap_content"
         />
 
+    <com.android.systemui.media.taptotransfer.receiver.ReceiverChipRippleView
+        android:id="@+id/icon_glow_ripple"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
+
+    <!-- Add a bottom margin to avoid the glow of the icon ripple from being cropped by screen
+     bounds while animating with the icon -->
     <com.android.internal.widget.CachingIconView
         android:id="@+id/app_icon"
         android:background="@drawable/media_ttt_chip_background_receiver"
@@ -34,6 +42,7 @@
         android:layout_height="@dimen/media_ttt_icon_size_receiver"
         android:layout_gravity="center|bottom"
         android:alpha="0.0"
+        android:layout_marginBottom="@dimen/media_ttt_receiver_icon_bottom_margin"
         />
 
 </FrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6202939..f122805 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1087,6 +1087,7 @@
          (112 - 40) / 2 = 36dp -->
     <dimen name="media_ttt_generic_icon_padding">36dp</dimen>
     <dimen name="media_ttt_receiver_vert_translation">40dp</dimen>
+    <dimen name="media_ttt_receiver_icon_bottom_margin">10dp</dimen>
 
     <!-- Window magnification -->
     <dimen name="magnification_border_drag_size">35dp</dimen>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 8a0fca0..28e786b 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -91,6 +91,9 @@
     static_libs: [
         "SystemUI-flag-types",
     ],
+    optimize: {
+        proguard_flags_files: ["proguard_flags.flags"],
+    },
     java_version: "1.8",
     min_sdk_version: "current",
 }
diff --git a/packages/SystemUI/shared/proguard_flags.flags b/packages/SystemUI/shared/proguard_flags.flags
new file mode 100644
index 0000000..08859cd
--- /dev/null
+++ b/packages/SystemUI/shared/proguard_flags.flags
@@ -0,0 +1 @@
+-keep class * implements com.android.systemui.flags.ParcelableFlag
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index fa484c7..a71fb56 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -37,7 +37,7 @@
     /**
      * Sent when overview is to be shown.
      */
-    void onOverviewShown(boolean triggeredFromAltTab, boolean forward) = 7;
+    void onOverviewShown(boolean triggeredFromAltTab) = 7;
 
     /**
      * Sent when overview is to be hidden.
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index 1454210..fb0c0a6 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -20,7 +20,7 @@
 import android.content.res.Configuration
 import android.graphics.PixelFormat
 import android.os.SystemProperties
-import android.util.DisplayMetrics
+import android.view.Surface
 import android.view.View
 import android.view.WindowManager
 import com.android.internal.annotations.VisibleForTesting
@@ -36,7 +36,6 @@
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.leak.RotationUtils
 import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -172,30 +171,28 @@
     }
 
     private fun layoutRipple() {
-        val displayMetrics = DisplayMetrics()
-        context.display.getRealMetrics(displayMetrics)
-        val width = displayMetrics.widthPixels
-        val height = displayMetrics.heightPixels
+        val bounds = windowManager.currentWindowMetrics.bounds
+        val width = bounds.width()
+        val height = bounds.height()
         val maxDiameter = Integer.max(width, height) * 2f
         rippleView.setMaxSize(maxDiameter, maxDiameter)
-        when (RotationUtils.getExactRotation(context)) {
-            RotationUtils.ROTATION_LANDSCAPE -> {
+        when (context.display.rotation) {
+            Surface.ROTATION_0 -> {
+                rippleView.setCenter(
+                        width * normalizedPortPosX, height * normalizedPortPosY)
+            }
+            Surface.ROTATION_90 -> {
                 rippleView.setCenter(
                         width * normalizedPortPosY, height * (1 - normalizedPortPosX))
             }
-            RotationUtils.ROTATION_UPSIDE_DOWN -> {
+            Surface.ROTATION_180 -> {
                 rippleView.setCenter(
                         width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY))
             }
-            RotationUtils.ROTATION_SEASCAPE -> {
+            Surface.ROTATION_270 -> {
                 rippleView.setCenter(
                         width * (1 - normalizedPortPosY), height * normalizedPortPosX)
             }
-            else -> {
-                // ROTATION_NONE
-                rippleView.setCenter(
-                        width * normalizedPortPosX, height * normalizedPortPosY)
-            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 5d85fc96..848f391 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -104,7 +104,7 @@
         unreleasedFlag(174148361, "notification_inline_reply_animation", teamfood = true)
 
     val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
-        unreleasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
+        releasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
 
     // TODO(b/263414400): Tracking Bug
     @JvmField
@@ -217,6 +217,11 @@
     val ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS =
         unreleasedFlag(226, "enable_wallet_contextual_loyalty_cards", teamfood = false)
 
+    // TODO(b/242908637): Tracking Bug
+    @JvmField
+    val WALLPAPER_FULLSCREEN_PREVIEW =
+        unreleasedFlag(227, "wallpaper_fullscreen_preview", teamfood = true)
+
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
     @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -230,7 +235,8 @@
 
     // TODO(b/258517050): Clean up after the feature is launched.
     @JvmField
-    val SMARTSPACE_DATE_WEATHER_DECOUPLED = unreleasedFlag(403, "smartspace_date_weather_decoupled")
+    val SMARTSPACE_DATE_WEATHER_DECOUPLED =
+        sysPropBooleanFlag(403, "persist.sysui.ss.dw_decoupled", default = false)
 
     // 500 - quick settings
 
@@ -463,6 +469,16 @@
     val WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM =
         unreleasedFlag(1206, "persist.wm.debug.predictive_back_bouncer_anim", teamfood = true)
 
+    // TODO(b/238475428): Tracking Bug
+    @JvmField
+    val WM_SHADE_ALLOW_BACK_GESTURE =
+        unreleasedFlag(1207, "persist.wm.debug.shade_allow_back_gesture", teamfood = false)
+
+    // TODO(b/238475428): Tracking Bug
+    @JvmField
+    val WM_SHADE_ANIMATE_BACK_GESTURE =
+        unreleasedFlag(1208, "persist.wm.debug.shade_animate_back_gesture", teamfood = true)
+
     // 1300 - screenshots
     // TODO(b/254513155): Tracking Bug
     @JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index c219380..9ddc575 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -363,6 +363,10 @@
                 name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
                 value = featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
             ),
+            KeyguardPickerFlag(
+                name = Contract.FlagsTable.FLAG_NAME_WALLPAPER_FULLSCREEN_PREVIEW,
+                value = featureFlags.isEnabled(Flags.WALLPAPER_FULLSCREEN_PREVIEW),
+            ),
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
index 5c65c8b..4827a16 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
@@ -230,7 +230,14 @@
 
     fun updateColorScheme(colorScheme: ColorScheme?): Boolean {
         var anyChanged = false
-        colorTransitions.forEach { anyChanged = it.updateColorScheme(colorScheme) || anyChanged }
+        colorTransitions.forEach {
+            val isChanged = it.updateColorScheme(colorScheme)
+
+            // Ignore changes to colorSeamless, since that is expected when toggling dark mode
+            if (it == colorSeamless) return@forEach
+
+            anyChanged = isChanged || anyChanged
+        }
         colorScheme?.let { mediaViewHolder.gutsViewHolder.colorScheme = colorScheme }
         return anyChanged
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index a9e1a4d..4803371 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -95,6 +95,7 @@
     private RecyclerView mDevicesRecyclerView;
     private LinearLayout mDeviceListLayout;
     private LinearLayout mCastAppLayout;
+    private LinearLayout mMediaMetadataSectionLayout;
     private Button mDoneButton;
     private Button mStopButton;
     private Button mAppButton;
@@ -240,6 +241,7 @@
         mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
         mHeaderIcon = mDialogView.requireViewById(R.id.header_icon);
         mDevicesRecyclerView = mDialogView.requireViewById(R.id.list_result);
+        mMediaMetadataSectionLayout = mDialogView.requireViewById(R.id.media_metadata_section);
         mDeviceListLayout = mDialogView.requireViewById(R.id.device_list);
         mDoneButton = mDialogView.requireViewById(R.id.done);
         mStopButton = mDialogView.requireViewById(R.id.stop);
@@ -255,21 +257,17 @@
         mDevicesRecyclerView.setLayoutManager(mLayoutManager);
         mDevicesRecyclerView.setAdapter(mAdapter);
         mDevicesRecyclerView.setHasFixedSize(false);
-        // Init header icon
-        mHeaderIcon.setOnClickListener(v -> onHeaderIconClick());
         // Init bottom buttons
         mDoneButton.setOnClickListener(v -> dismiss());
         mStopButton.setOnClickListener(v -> {
             mMediaOutputController.releaseSession();
             dismiss();
         });
-        mAppButton.setOnClickListener(v -> {
-            mBroadcastSender.closeSystemDialogs();
-            if (mMediaOutputController.getAppLaunchIntent() != null) {
-                mContext.startActivity(mMediaOutputController.getAppLaunchIntent());
-            }
-            dismiss();
-        });
+        mAppButton.setOnClickListener(v -> mMediaOutputController.tryToLaunchMediaApplication());
+        if (mMediaOutputController.isAdvancedLayoutSupported()) {
+            mMediaMetadataSectionLayout.setOnClickListener(
+                    v -> mMediaOutputController.tryToLaunchMediaApplication());
+        }
     }
 
     @Override
@@ -560,7 +558,7 @@
 
     @Override
     public void dismissDialog() {
-        dismiss();
+        mBroadcastSender.closeSystemDialogs();
     }
 
     void onHeaderIconClick() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 9cf672b..1587e62 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -382,6 +382,15 @@
         return mContext.getPackageManager().getLaunchIntentForPackage(mPackageName);
     }
 
+    void tryToLaunchMediaApplication() {
+        Intent launchIntent = getAppLaunchIntent();
+        if (launchIntent != null) {
+            launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            mCallback.dismissDialog();
+            mContext.startActivity(launchIntent);
+        }
+    }
+
     CharSequence getHeaderTitle() {
         if (mMediaController != null) {
             final MediaMetadata metadata = mMediaController.getMetadata();
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 889147b..60dd5da 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -30,8 +30,8 @@
 import android.view.ViewGroup
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
+import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
 import com.android.internal.widget.CachingIconView
-import com.android.settingslib.Utils
 import com.android.systemui.R
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.ui.binder.TintedIconViewBinder
@@ -78,6 +78,7 @@
         private val viewUtil: ViewUtil,
         wakeLockBuilder: WakeLock.Builder,
         systemClock: SystemClock,
+        private val rippleController: MediaTttReceiverRippleController,
 ) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttLogger<ChipReceiverInfo>>(
         context,
         logger,
@@ -114,9 +115,6 @@
         }
     }
 
-    private var maxRippleWidth: Float = 0f
-    private var maxRippleHeight: Float = 0f
-
     private fun updateMediaTapToTransferReceiverDisplay(
         @StatusBarManager.MediaTransferReceiverState displayState: Int,
         routeInfo: MediaRoute2Info,
@@ -201,41 +199,44 @@
 
         val iconView = currentView.getAppIconView()
         iconView.setPadding(iconPadding, iconPadding, iconPadding, iconPadding)
+        iconView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_ASSERTIVE
         TintedIconViewBinder.bind(iconInfo.toTintedIcon(), iconView)
     }
 
     override fun animateViewIn(view: ViewGroup) {
         val appIconView = view.getAppIconView()
-        appIconView.animate()
-                .translationYBy(-1 * getTranslationAmount().toFloat())
-                .setDuration(ICON_TRANSLATION_ANIM_DURATION)
-                .start()
-        appIconView.animate()
-                .alpha(1f)
-                .setDuration(ICON_ALPHA_ANIM_DURATION)
-                .start()
-        // Using withEndAction{} doesn't apply a11y focus when screen is unlocked.
-        appIconView.postOnAnimation { view.requestAccessibilityFocus() }
-        expandRipple(view.requireViewById(R.id.ripple))
+        val iconRippleView: ReceiverChipRippleView = view.requireViewById(R.id.icon_glow_ripple)
+        val rippleView: ReceiverChipRippleView = view.requireViewById(R.id.ripple)
+        animateViewTranslationAndFade(appIconView, -1 * getTranslationAmount(), 1f)
+        animateViewTranslationAndFade(iconRippleView, -1 * getTranslationAmount(), 1f)
+        rippleController.expandToInProgressState(rippleView, iconRippleView)
     }
 
     override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
         val appIconView = view.getAppIconView()
-        appIconView.animate()
-                .translationYBy(getTranslationAmount().toFloat())
-                .setDuration(ICON_TRANSLATION_ANIM_DURATION)
-                .start()
-        appIconView.animate()
-                .alpha(0f)
-                .setDuration(ICON_ALPHA_ANIM_DURATION)
-                .start()
-
+        val iconRippleView: ReceiverChipRippleView = view.requireViewById(R.id.icon_glow_ripple)
         val rippleView: ReceiverChipRippleView = view.requireViewById(R.id.ripple)
         if (removalReason == ChipStateReceiver.TRANSFER_TO_RECEIVER_SUCCEEDED.name &&
                 mediaTttFlags.isMediaTttReceiverSuccessRippleEnabled()) {
-            expandRippleToFull(rippleView, onAnimationEnd)
+            rippleController.expandToSuccessState(rippleView, onAnimationEnd)
+            animateViewTranslationAndFade(
+                iconRippleView,
+                -1 * getTranslationAmount(),
+                0f,
+                translationDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
+                alphaDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
+            )
+            animateViewTranslationAndFade(
+                appIconView,
+                -1 * getTranslationAmount(),
+                0f,
+                translationDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
+                alphaDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
+            )
         } else {
-            rippleView.collapseRipple(onAnimationEnd)
+            rippleController.collapseRipple(rippleView, onAnimationEnd)
+            animateViewTranslationAndFade(iconRippleView, getTranslationAmount(), 0f)
+            animateViewTranslationAndFade(appIconView, getTranslationAmount(), 0f)
         }
     }
 
@@ -245,74 +246,41 @@
         viewUtil.setRectToViewWindowLocation(view.getAppIconView(), outRect)
     }
 
+    /** Animation of view translation and fading. */
+    private fun animateViewTranslationAndFade(
+        view: View,
+        translationYBy: Float,
+        alphaEndValue: Float,
+        translationDuration: Long = ICON_TRANSLATION_ANIM_DURATION,
+        alphaDuration: Long = ICON_ALPHA_ANIM_DURATION,
+    ) {
+        view.animate()
+            .translationYBy(translationYBy)
+            .setDuration(translationDuration)
+            .start()
+        view.animate()
+            .alpha(alphaEndValue)
+            .setDuration(alphaDuration)
+            .start()
+    }
+
     /** Returns the amount that the chip will be translated by in its intro animation. */
-    private fun getTranslationAmount(): Int {
-        return context.resources.getDimensionPixelSize(R.dimen.media_ttt_receiver_vert_translation)
-    }
-
-    private fun expandRipple(rippleView: ReceiverChipRippleView) {
-        if (rippleView.rippleInProgress()) {
-            // Skip if ripple is still playing
-            return
-        }
-
-        // In case the device orientation changes, we need to reset the layout.
-        rippleView.addOnLayoutChangeListener (
-            View.OnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
-                if (v == null) return@OnLayoutChangeListener
-
-                val layoutChangedRippleView = v as ReceiverChipRippleView
-                layoutRipple(layoutChangedRippleView)
-                layoutChangedRippleView.invalidate()
-            }
-        )
-        rippleView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
-            override fun onViewDetachedFromWindow(view: View?) {}
-
-            override fun onViewAttachedToWindow(view: View?) {
-                if (view == null) {
-                    return
-                }
-                val attachedRippleView = view as ReceiverChipRippleView
-                layoutRipple(attachedRippleView)
-                attachedRippleView.expandRipple()
-                attachedRippleView.removeOnAttachStateChangeListener(this)
-            }
-        })
-    }
-
-    private fun layoutRipple(rippleView: ReceiverChipRippleView, isFullScreen: Boolean = false) {
-        val windowBounds = windowManager.currentWindowMetrics.bounds
-        val height = windowBounds.height().toFloat()
-        val width = windowBounds.width().toFloat()
-
-        if (isFullScreen) {
-            maxRippleHeight = height * 2f
-            maxRippleWidth = width * 2f
-        } else {
-            maxRippleHeight = height / 2f
-            maxRippleWidth = width / 2f
-        }
-        rippleView.setMaxSize(maxRippleWidth, maxRippleHeight)
-        // Center the ripple on the bottom of the screen in the middle.
-        rippleView.setCenter(width * 0.5f, height)
-        val color = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
-        rippleView.setColor(color, 70)
+    private fun getTranslationAmount(): Float {
+        return rippleController.getRippleSize() * 0.5f -
+            rippleController.getReceiverIconSize()
     }
 
     private fun View.getAppIconView(): CachingIconView {
         return this.requireViewById(R.id.app_icon)
     }
 
-    private fun expandRippleToFull(rippleView: ReceiverChipRippleView, onAnimationEnd: Runnable?) {
-        layoutRipple(rippleView, true)
-        rippleView.expandToFull(maxRippleHeight, onAnimationEnd)
+    companion object {
+        private const val ICON_TRANSLATION_ANIM_DURATION = 500L
+        private const val ICON_TRANSLATION_SUCCEEDED_DURATION = 167L
+        private val ICON_ALPHA_ANIM_DURATION = 5.frames
     }
 }
 
-val ICON_TRANSLATION_ANIM_DURATION = 30.frames
-val ICON_ALPHA_ANIM_DURATION = 5.frames
-
 data class ChipReceiverInfo(
     val routeInfo: MediaRoute2Info,
     val appIconDrawableOverride: Drawable?,
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt
new file mode 100644
index 0000000..5013802
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.media.taptotransfer.receiver
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.view.View
+import android.view.WindowManager
+import com.android.settingslib.Utils
+import com.android.systemui.R
+import javax.inject.Inject
+
+/**
+ * A controller responsible for the animation of the ripples shown in media tap-to-transfer on the
+ * receiving device.
+ */
+class MediaTttReceiverRippleController
+@Inject
+constructor(
+    private val context: Context,
+    private val windowManager: WindowManager,
+) {
+
+    private var maxRippleWidth: Float = 0f
+    private var maxRippleHeight: Float = 0f
+
+    /** Expands the icon and main ripple to in-progress state */
+    fun expandToInProgressState(
+        mainRippleView: ReceiverChipRippleView,
+        iconRippleView: ReceiverChipRippleView,
+    ) {
+        expandRipple(mainRippleView, isIconRipple = false)
+        expandRipple(iconRippleView, isIconRipple = true)
+    }
+
+    private fun expandRipple(rippleView: ReceiverChipRippleView, isIconRipple: Boolean) {
+        if (rippleView.rippleInProgress()) {
+            // Skip if ripple is still playing
+            return
+        }
+
+        // In case the device orientation changes, we need to reset the layout.
+        rippleView.addOnLayoutChangeListener(
+            View.OnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
+                if (v == null) return@OnLayoutChangeListener
+
+                val layoutChangedRippleView = v as ReceiverChipRippleView
+                if (isIconRipple) {
+                    layoutIconRipple(layoutChangedRippleView)
+                } else {
+                    layoutRipple(layoutChangedRippleView)
+                }
+                layoutChangedRippleView.invalidate()
+            }
+        )
+        rippleView.addOnAttachStateChangeListener(
+            object : View.OnAttachStateChangeListener {
+                override fun onViewDetachedFromWindow(view: View?) {}
+
+                override fun onViewAttachedToWindow(view: View?) {
+                    if (view == null) {
+                        return
+                    }
+                    val attachedRippleView = view as ReceiverChipRippleView
+                    if (isIconRipple) {
+                        layoutIconRipple(attachedRippleView)
+                    } else {
+                        layoutRipple(attachedRippleView)
+                    }
+                    attachedRippleView.expandRipple()
+                    attachedRippleView.removeOnAttachStateChangeListener(this)
+                }
+            }
+        )
+    }
+
+    /** Expands the ripple to cover the screen. */
+    fun expandToSuccessState(rippleView: ReceiverChipRippleView, onAnimationEnd: Runnable?) {
+        layoutRipple(rippleView, isFullScreen = true)
+        rippleView.expandToFull(maxRippleHeight, onAnimationEnd)
+    }
+
+    /** Collapses the ripple. */
+    fun collapseRipple(rippleView: ReceiverChipRippleView, onAnimationEnd: Runnable? = null) {
+        rippleView.collapseRipple(onAnimationEnd)
+    }
+
+    private fun layoutRipple(rippleView: ReceiverChipRippleView, isFullScreen: Boolean = false) {
+        val windowBounds = windowManager.currentWindowMetrics.bounds
+        val height = windowBounds.height().toFloat()
+        val width = windowBounds.width().toFloat()
+
+        if (isFullScreen) {
+            maxRippleHeight = height * 2f
+            maxRippleWidth = width * 2f
+        } else {
+            maxRippleHeight = getRippleSize()
+            maxRippleWidth = getRippleSize()
+        }
+        rippleView.setMaxSize(maxRippleWidth, maxRippleHeight)
+        // Center the ripple on the bottom of the screen in the middle.
+        rippleView.setCenter(width * 0.5f, height)
+        rippleView.setColor(getRippleColor(), RIPPLE_OPACITY)
+    }
+
+    private fun layoutIconRipple(iconRippleView: ReceiverChipRippleView) {
+        val windowBounds = windowManager.currentWindowMetrics.bounds
+        val height = windowBounds.height().toFloat()
+        val width = windowBounds.width().toFloat()
+        val radius = getReceiverIconSize().toFloat()
+
+        iconRippleView.setMaxSize(radius * 0.8f, radius * 0.8f)
+        iconRippleView.setCenter(
+            width * 0.5f,
+            height - getReceiverIconSize() * 0.5f - getReceiverIconBottomMargin()
+        )
+        iconRippleView.setColor(getRippleColor(), RIPPLE_OPACITY)
+    }
+
+    private fun getRippleColor(): Int {
+        var colorStateList =
+            ColorStateList.valueOf(
+                Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
+            )
+        return colorStateList.withLStar(TONE_PERCENT).defaultColor
+    }
+
+    /** Returns the size of the ripple. */
+    internal fun getRippleSize(): Float {
+        return getReceiverIconSize() * 4f
+    }
+
+    /** Returns the size of the icon of the receiver. */
+    internal fun getReceiverIconSize(): Int {
+        return context.resources.getDimensionPixelSize(R.dimen.media_ttt_icon_size_receiver)
+    }
+
+    /** Return the bottom margin of the icon of the receiver. */
+    internal fun getReceiverIconBottomMargin(): Int {
+        // Adding a margin to make sure ripple behind the icon is not cut by the screen bounds.
+        return context.resources.getDimensionPixelSize(
+            R.dimen.media_ttt_receiver_icon_bottom_margin
+        )
+    }
+
+    companion object {
+        const val RIPPLE_OPACITY = 70
+        const val TONE_PERCENT = 95f
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
index 87b2528..f8785fc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
@@ -33,14 +33,14 @@
     private var isStarted: Boolean
 
     init {
-        setupShader(RippleShader.RippleShape.ELLIPSE)
+        setupShader(RippleShader.RippleShape.CIRCLE)
         setRippleFill(true)
         setSparkleStrength(0f)
-        duration = 3000L
         isStarted = false
     }
 
     fun expandRipple(onAnimationEnd: Runnable? = null) {
+        duration = DEFAULT_DURATION
         isStarted = true
         super.startRipple(onAnimationEnd)
     }
@@ -50,6 +50,7 @@
         if (!isStarted) {
             return // Ignore if ripple is not started yet.
         }
+        duration = DEFAULT_DURATION
         // Reset all listeners to animator.
         animator.removeAllListeners()
         animator.addListener(object : AnimatorListenerAdapter() {
@@ -74,6 +75,7 @@
         setRippleFill(false)
 
         val startingPercentage = calculateStartingPercentage(newHeight)
+        animator.duration = EXPAND_TO_FULL_DURATION
         animator.addUpdateListener { updateListener ->
             val now = updateListener.currentPlayTime
             val progress = updateListener.animatedValue as Float
@@ -100,4 +102,9 @@
         val remainingPercentage = (1 - ratio).toDouble().pow(1 / 3.toDouble()).toFloat()
         return 1 - remainingPercentage
     }
+
+    companion object {
+        const val DEFAULT_DURATION = 333L
+        const val EXPAND_TO_FULL_DURATION = 1000L
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 06db5ed..32dee92 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -744,6 +744,7 @@
         setWindowVisible(isNavBarWindowVisible());
         mView.setBehavior(mBehavior);
         setNavBarMode(mNavBarMode);
+        repositionNavigationBar(mCurrentRotation);
         mView.setUpdateActiveTouchRegionsCallback(
                 () -> mOverviewProxyService.onActiveNavBarRegionChanges(
                         getButtonLocations(
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index c335a6d..5ea1c0b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -59,11 +59,11 @@
     }
 
     @Override
-    public void showRecentApps(boolean triggeredFromAltTab, boolean forward) {
+    public void showRecentApps(boolean triggeredFromAltTab) {
         IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
         if (overviewProxy != null) {
             try {
-                overviewProxy.onOverviewShown(triggeredFromAltTab, forward);
+                overviewProxy.onOverviewShown(triggeredFromAltTab);
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to send overview show event to launcher.", e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 95d6c18..b041f95 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -65,14 +65,14 @@
     }
 
     @Override
-    public void showRecentApps(boolean triggeredFromAltTab, boolean forward) {
+    public void showRecentApps(boolean triggeredFromAltTab) {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
         if (!isUserSetup()) {
             return;
         }
 
-        mImpl.showRecentApps(triggeredFromAltTab, forward);
+        mImpl.showRecentApps(triggeredFromAltTab);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
index 010ceda..8848dbb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
@@ -31,7 +31,7 @@
 
     default void preloadRecentApps() {}
     default void cancelPreloadRecentApps() {}
-    default void showRecentApps(boolean triggeredFromAltTab, boolean forward) {}
+    default void showRecentApps(boolean triggeredFromAltTab) {}
     default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {}
     default void toggleRecentApps() {}
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index b532c13..d11f9da 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2097,7 +2097,17 @@
         }
     }
 
-    public void expandWithoutQs() {
+    /**
+     * Expand shade so that notifications are visible.
+     * Non-split shade: just expanding shade or collapsing QS when they're expanded.
+     * Split shade: only expanding shade, notifications are always visible
+     *
+     * Called when `adb shell cmd statusbar expand-notifications` is executed.
+     */
+    public void expandShadeToNotifications() {
+        if (mSplitShadeEnabled && (isShadeFullyOpen() || isExpanding())) {
+            return;
+        }
         if (isQsExpanded()) {
             flingSettings(0 /* velocity */, FLING_COLLAPSE);
         } else {
@@ -5536,7 +5546,7 @@
 
         @Override
         public void flingTopOverscroll(float velocity, boolean open) {
-            // in split shade mode we want to expand/collapse QS only when touch happens within QS
+            // in split shade touches affect QS only when touch happens within QS
             if (isSplitShadeAndTouchXOutsideQs(mInitialTouchX)) {
                 return;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 7556750..a0a7586 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -53,7 +53,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.Pair;
-import android.util.Slog;
 import android.util.SparseArray;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.WindowInsets.Type.InsetsType;
@@ -228,7 +227,7 @@
          */
         default void setImeWindowStatus(int displayId, IBinder token,  int vis,
                 @BackDispositionMode int backDisposition, boolean showImeSwitcher) { }
-        default void showRecentApps(boolean triggeredFromAltTab, boolean forward) { }
+        default void showRecentApps(boolean triggeredFromAltTab) { }
         default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { }
         default void toggleRecentApps() { }
         default void toggleSplitScreen() { }
@@ -695,11 +694,11 @@
         }
     }
 
-    public void showRecentApps(boolean triggeredFromAltTab, boolean forward) {
+    public void showRecentApps(boolean triggeredFromAltTab) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_RECENT_APPS);
-            mHandler.obtainMessage(MSG_SHOW_RECENT_APPS, triggeredFromAltTab ? 1 : 0,
-                    forward ? 1 : 0, null).sendToTarget();
+            mHandler.obtainMessage(MSG_SHOW_RECENT_APPS, triggeredFromAltTab ? 1 : 0, 0,
+                    null).sendToTarget();
         }
     }
 
@@ -1271,8 +1270,7 @@
     public void showMediaOutputSwitcher(String packageName) {
         int callingUid = Binder.getCallingUid();
         if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
-            Slog.e(TAG, "Call only allowed from system server.");
-            return;
+            throw new SecurityException("Call only allowed from system server.");
         }
         synchronized (mLock) {
             SomeArgs args = SomeArgs.obtain();
@@ -1407,7 +1405,7 @@
                     break;
                 case MSG_SHOW_RECENT_APPS:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).showRecentApps(msg.arg1 != 0, msg.arg2 != 0);
+                        mCallbacks.get(i).showRecentApps(msg.arg1 != 0);
                     }
                     break;
                 case MSG_HIDE_RECENT_APPS:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 76252d0..e996b78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -114,10 +114,10 @@
             .onStart { emit(Unit) }
             // for each change, lookup the new value
             .map {
-                secureSettings.getBoolForUser(
+                secureSettings.getIntForUser(
                     Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
                     UserHandle.USER_CURRENT,
-                )
+                ) == 1
             }
             // perform lookups on the bg thread pool
             .flowOn(bgDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 66632e4..856d7de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -218,7 +218,7 @@
             return;
         }
 
-        mNotificationPanelViewController.expandWithoutQs();
+        mNotificationPanelViewController.expandShadeToNotifications();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 001da6f..f7b8745 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -1146,7 +1146,7 @@
             if (hideImmediately) {
                 mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
             } else {
-                mNotificationPanelViewController.expandWithoutQs();
+                mNotificationPanelViewController.expandShadeToNotifications();
             }
         }
         return;
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 f8c17e8..4866f73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -303,7 +303,8 @@
                     mEntry.mRemoteEditImeVisible = editTextRootWindowInsets != null
                             && editTextRootWindowInsets.isVisible(WindowInsets.Type.ime());
                     if (!mEntry.mRemoteEditImeVisible && !mEditText.mShowImeOnInputConnection) {
-                        mController.removeRemoteInput(mEntry, mToken);
+                        // Pass null to ensure all inputs are cleared for this entry b/227115380
+                        mController.removeRemoteInput(mEntry, null);
                     }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
index 8d5e01c..9050dad 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
@@ -29,13 +29,14 @@
 import android.os.Handler
 import android.os.UserHandle
 import android.util.Log
-import android.view.InputDevice
 import androidx.core.app.NotificationCompat
 import androidx.core.app.NotificationManagerCompat
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.hardware.hasInputDevice
+import com.android.systemui.shared.hardware.isAnyStylusSource
 import com.android.systemui.util.NotificationChannels
 import java.text.NumberFormat
 import javax.inject.Inject
@@ -150,10 +151,7 @@
     }
 
     private fun hasConnectedBluetoothStylus(): Boolean {
-        // TODO(b/257936830): get bt address once input api available
-        return inputManager.inputDeviceIds.any { deviceId ->
-            inputManager.getInputDevice(deviceId).supportsSource(InputDevice.SOURCE_STYLUS)
-        }
+        return inputManager.hasInputDevice { it.isAnyStylusSource && it.bluetoothAddress != null }
     }
 
     private fun getPendingBroadcast(action: String): PendingIntent? {
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
index 0b2f004..31f35fc 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
@@ -1,15 +1,17 @@
 /*
  * Copyright (C) 2017 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
+ * 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.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.systemui.util.leak;
@@ -26,7 +28,27 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-public class RotationUtils {
+/**
+ * Utility class that provides device orientation.
+ *
+ * <p>Consider using {@link Surface.Rotation} or add a function that respects device aspect ratio
+ * and {@code android.internal.R.bool.config_reverseDefaultRotation}.
+ *
+ * <p>If you only care about the rotation, use {@link Surface.Rotation}, as it always gives the
+ * counter clock-wise rotation. (e.g. If you have a device that has a charging port at the bottom,
+ * rotating three times in counter clock direction will give you {@link Surface#ROTATION_270} while
+ * having the charging port on the left side of the device.)
+ *
+ * <p>If you need whether the device is in portrait or landscape (or their opposites), please add a
+ * function here that respects the device aspect ratio and
+ * {@code android.internal.R.bool.config_reverseDefaultRotation} together.
+ *
+ * <p>Note that {@code android.internal.R.bool.config_reverseDefaultRotation} does not change the
+ * winding order. In other words, the rotation order (counter clock-wise) will remain the same. It
+ * only flips the device orientation, such that portrait becomes upside down, landscape becomes
+ * seascape.
+ */
+public final class RotationUtils {
 
     public static final int ROTATION_NONE = 0;
     public static final int ROTATION_LANDSCAPE = 1;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index d159714..d6cafcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -16,18 +16,23 @@
 
 package com.android.systemui.charging
 
+import android.graphics.Rect
 import android.testing.AndroidTestingRunner
+import android.view.Surface
 import android.view.View
 import android.view.WindowManager
+import android.view.WindowMetrics
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
-import com.android.systemui.surfaceeffects.ripple.RippleView
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.surfaceeffects.ripple.RippleView
+import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import org.junit.Before
 import org.junit.Test
@@ -35,12 +40,12 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers
 import org.mockito.Mock
+import org.mockito.Mockito.`when`
 import org.mockito.Mockito.any
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -54,6 +59,7 @@
     @Mock private lateinit var rippleView: RippleView
     @Mock private lateinit var windowManager: WindowManager
     @Mock private lateinit var uiEventLogger: UiEventLogger
+    @Mock private lateinit var windowMetrics: WindowMetrics
     private val systemClock = FakeSystemClock()
 
     @Before
@@ -66,6 +72,9 @@
         rippleView.setupShader()
         controller.rippleView = rippleView // Replace the real ripple view with a mock instance
         controller.registerCallbacks()
+
+        `when`(windowMetrics.bounds).thenReturn(Rect(0, 0, 100, 100))
+        `when`(windowManager.currentWindowMetrics).thenReturn(windowMetrics)
     }
 
     @Test
@@ -164,4 +173,63 @@
         verify(rippleView, never()).addOnAttachStateChangeListener(attachListenerCaptor.capture())
         verify(windowManager, never()).addView(eq(rippleView), any<WindowManager.LayoutParams>())
     }
+
+    @Test
+    fun testRipple_layoutsCorrectly() {
+        // Sets the correct ripple size.
+        val width = 100
+        val height = 200
+        whenever(windowMetrics.bounds).thenReturn(Rect(0, 0, width, height))
+
+        // Trigger ripple.
+        val captor = ArgumentCaptor
+                .forClass(BatteryController.BatteryStateChangeCallback::class.java)
+        verify(batteryController).addCallback(captor.capture())
+
+        captor.value.onBatteryLevelChanged(
+                /* unusedBatteryLevel= */ 0,
+                /* plugged in= */ true,
+                /* charging= */ false)
+
+        val attachListenerCaptor =
+                ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
+        verify(rippleView).addOnAttachStateChangeListener(attachListenerCaptor.capture())
+        verify(windowManager).addView(eq(rippleView), any<WindowManager.LayoutParams>())
+
+        val runnableCaptor =
+                ArgumentCaptor.forClass(Runnable::class.java)
+        attachListenerCaptor.value.onViewAttachedToWindow(rippleView)
+        verify(rippleView).startRipple(runnableCaptor.capture())
+
+        // Verify size and center position.
+        val maxSize = 400f // Double the max value between width and height.
+        verify(rippleView).setMaxSize(maxWidth = maxSize, maxHeight = maxSize)
+
+        val normalizedPortPosX =
+                context.resources.getFloat(R.dimen.physical_charger_port_location_normalized_x)
+        val normalizedPortPosY =
+                context.resources.getFloat(R.dimen.physical_charger_port_location_normalized_y)
+        val expectedCenterX: Float
+        val expectedCenterY: Float
+        when (context.display.rotation) {
+            Surface.ROTATION_90 -> {
+                expectedCenterX = width * normalizedPortPosY
+                expectedCenterY = height * (1 - normalizedPortPosX)
+            }
+            Surface.ROTATION_180 -> {
+                expectedCenterX = width * (1 - normalizedPortPosX)
+                expectedCenterY = height * (1 - normalizedPortPosY)
+            }
+            Surface.ROTATION_270 -> {
+                expectedCenterX = width * (1 - normalizedPortPosY)
+                expectedCenterY = height * normalizedPortPosX
+            }
+            else -> { // Surface.ROTATION_0
+                expectedCenterX = width * normalizedPortPosX
+                expectedCenterY = height * normalizedPortPosY
+            }
+        }
+
+        verify(rippleView).setCenter(expectedCenterX, expectedCenterY)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 0a03b2c..c0af0cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -173,6 +173,7 @@
                         set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
                         set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
                         set(Flags.REVAMPED_WALLPAPER_UI, true)
+                        set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
                     },
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 094d69a..9a0bd9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -243,6 +243,13 @@
     }
 
     @Test
+    public void dismissDialog_closesDialogByBroadcastSender() {
+        mMediaOutputBaseDialogImpl.dismissDialog();
+
+        verify(mBroadcastSender).closeSystemDialogs();
+    }
+
+    @Test
     public void whenBroadcasting_verifyLeBroadcastServiceCallBackIsRegisteredAndUnregistered() {
         when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
                 mLocalBluetoothLeBroadcast);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index f5432e2..117751c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -252,6 +252,13 @@
     }
 
     @Test
+    public void tryToLaunchMediaApplication_nullIntent_skip() {
+        mMediaOutputController.tryToLaunchMediaApplication();
+
+        verify(mCb, never()).dismissDialog();
+    }
+
+    @Test
     public void onDevicesUpdated_unregistersNearbyDevicesCallback() throws RemoteException {
         mMediaOutputController.start(mCb);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index 9c4e849..b3e621e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -48,6 +48,7 @@
     viewUtil: ViewUtil,
     wakeLockBuilder: WakeLock.Builder,
     systemClock: SystemClock,
+    rippleController: MediaTttReceiverRippleController,
 ) :
     MediaTttChipControllerReceiver(
         commandQueue,
@@ -65,6 +66,7 @@
         viewUtil,
         wakeLockBuilder,
         systemClock,
+        rippleController,
     ) {
     override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
         // Just bypass the animation in tests
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index cefc742..5e40898 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -85,6 +85,8 @@
     private lateinit var windowManager: WindowManager
     @Mock
     private lateinit var commandQueue: CommandQueue
+    @Mock
+    private lateinit var rippleController: MediaTttReceiverRippleController
     private lateinit var commandQueueCallback: CommandQueue.Callbacks
     private lateinit var fakeAppIconDrawable: Drawable
     private lateinit var uiEventLoggerFake: UiEventLoggerFake
@@ -134,6 +136,7 @@
             viewUtil,
             fakeWakeLockBuilder,
             fakeClock,
+            rippleController,
         )
         controllerReceiver.start()
 
@@ -163,6 +166,7 @@
             viewUtil,
             fakeWakeLockBuilder,
             fakeClock,
+            rippleController,
         )
         controllerReceiver.start()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index fc7cd89..0000c32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -209,9 +209,9 @@
 
     @Test
     public void testShowRecentApps() {
-        mCommandQueue.showRecentApps(true, false);
+        mCommandQueue.showRecentApps(true);
         waitForIdleSync();
-        verify(mCallbacks).showRecentApps(eq(true), eq(false));
+        verify(mCallbacks).showRecentApps(eq(true));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index be6b1dc..2686238 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -321,7 +321,7 @@
         val testDispatcher = UnconfinedTestDispatcher()
         val testScope = TestScope(testDispatcher)
         val fakeSettings = FakeSettings().apply {
-            putBool(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, true)
+            putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1)
         }
         val seenNotificationsProvider = SeenNotificationsProviderImpl()
         val keyguardCoordinator =
@@ -372,14 +372,14 @@
 
         var showOnlyUnseenNotifsOnKeyguardSetting: Boolean
             get() =
-                fakeSettings.getBoolForUser(
+                fakeSettings.getIntForUser(
                     Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
                     UserHandle.USER_CURRENT,
-                )
+                ) == 1
             set(value) {
-                fakeSettings.putBoolForUser(
+                fakeSettings.putIntForUser(
                     Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-                    value,
+                    if (value) 1 else 2,
                     UserHandle.USER_CURRENT,
                 )
             }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 6fc60f1..52bd424 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -147,7 +147,7 @@
 
         // Trying to open it does nothing.
         mSbcqCallbacks.animateExpandNotificationsPanel();
-        verify(mNotificationPanelViewController, never()).expandWithoutQs();
+        verify(mNotificationPanelViewController, never()).expandShadeToNotifications();
         mSbcqCallbacks.animateExpandSettingsPanel(null);
         verify(mNotificationPanelViewController, never()).expand(anyBoolean());
     }
@@ -165,7 +165,7 @@
 
         // Can now be opened.
         mSbcqCallbacks.animateExpandNotificationsPanel();
-        verify(mNotificationPanelViewController).expandWithoutQs();
+        verify(mNotificationPanelViewController).expandShadeToNotifications();
         mSbcqCallbacks.animateExpandSettingsPanel(null);
         verify(mNotificationPanelViewController).expandWithQs();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
index 1e81dc7..e1668e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
@@ -36,7 +36,6 @@
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertEquals
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -79,7 +78,7 @@
         whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf())
         whenever(inputManager.getInputDevice(0)).thenReturn(btStylusDevice)
         whenever(btStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
-        // whenever(btStylusDevice.bluetoothAddress).thenReturn("SO:ME:AD:DR:ES")
+        whenever(btStylusDevice.bluetoothAddress).thenReturn("SO:ME:AD:DR:ES")
 
         stylusUsiPowerUi = StylusUsiPowerUI(contextSpy, notificationManager, inputManager, handler)
         broadcastReceiver = stylusUsiPowerUi.receiver
@@ -179,7 +178,6 @@
     }
 
     @Test
-    @Ignore("TODO(b/257936830): get bt address once input api available")
     fun refresh_hasConnectedBluetoothStylus_cancelsNotification() {
         whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(0))
 
@@ -189,7 +187,6 @@
     }
 
     @Test
-    @Ignore("TODO(b/257936830): get bt address once input api available")
     fun refresh_hasConnectedBluetoothStylus_existingNotification_cancelsNotification() {
         stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f))
         whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(0))
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
new file mode 100644
index 0000000..56e777f
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.datatransfer.contextsync;
+
+/** Callback for call metadata syncing. */
+public abstract class CallMetadataSyncCallback {
+
+    abstract void processCallControlAction(int crossDeviceCallId, int callControlAction);
+
+    abstract void requestCrossDeviceSync(int userId);
+}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
new file mode 100644
index 0000000..077fd2a
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.datatransfer.contextsync;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.telecom.Call;
+import android.telecom.CallAudioState;
+import android.telecom.VideoProfile;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.ByteArrayOutputStream;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+/** Data holder for a telecom call and additional metadata. */
+public class CrossDeviceCall {
+
+    private static final String TAG = "CrossDeviceCall";
+
+    private static final int APP_ICON_BITMAP_DIMENSION = 256;
+
+    private static final AtomicLong sNextId = new AtomicLong(1);
+
+    private final long mId;
+    private final Call mCall;
+    @VisibleForTesting boolean mIsEnterprise;
+    @VisibleForTesting boolean mIsOtt;
+    private String mCallingAppName;
+    private byte[] mCallingAppIcon;
+    private String mCallerDisplayName;
+    private int mStatus = android.companion.Telecom.Call.UNKNOWN_STATUS;
+    private String mContactDisplayName;
+    private boolean mIsMuted;
+    private final Set<Integer> mControls = new HashSet<>();
+
+    public CrossDeviceCall(PackageManager packageManager, Call call,
+            CallAudioState callAudioState) {
+        mId = sNextId.getAndIncrement();
+        mCall = call;
+        final String callingAppPackageName = call != null
+                ? call.getDetails().getAccountHandle().getComponentName().getPackageName() : null;
+        mIsOtt = call != null
+                && (call.getDetails().getCallCapabilities() & Call.Details.PROPERTY_SELF_MANAGED)
+                == Call.Details.PROPERTY_SELF_MANAGED;
+        mIsEnterprise = call != null
+                && (call.getDetails().getCallProperties() & Call.Details.PROPERTY_ENTERPRISE_CALL)
+                == Call.Details.PROPERTY_ENTERPRISE_CALL;
+        try {
+            final ApplicationInfo applicationInfo = packageManager
+                    .getApplicationInfo(callingAppPackageName,
+                            PackageManager.ApplicationInfoFlags.of(0));
+            mCallingAppName = packageManager.getApplicationLabel(applicationInfo).toString();
+            mCallingAppIcon = renderDrawableToByteArray(
+                    packageManager.getApplicationIcon(applicationInfo));
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.e(TAG, "Could not get application info for package " + callingAppPackageName, e);
+        }
+        mIsMuted = callAudioState != null && callAudioState.isMuted();
+        if (call != null) {
+            updateCallDetails(call.getDetails());
+        }
+    }
+
+    private byte[] renderDrawableToByteArray(Drawable drawable) {
+        if (drawable instanceof BitmapDrawable) {
+            // Can't recycle the drawable's bitmap, so handle separately
+            final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+            if (bitmap.getWidth() > APP_ICON_BITMAP_DIMENSION
+                    || bitmap.getHeight() > APP_ICON_BITMAP_DIMENSION) {
+                // Downscale, as the original drawable bitmap is too large.
+                final Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap,
+                        APP_ICON_BITMAP_DIMENSION, APP_ICON_BITMAP_DIMENSION, /* filter= */ true);
+                final byte[] renderedBitmap = renderBitmapToByteArray(scaledBitmap);
+                scaledBitmap.recycle();
+                return renderedBitmap;
+            }
+            return renderBitmapToByteArray(bitmap);
+        }
+        final Bitmap bitmap = Bitmap.createBitmap(APP_ICON_BITMAP_DIMENSION,
+                APP_ICON_BITMAP_DIMENSION,
+                Bitmap.Config.ARGB_8888);
+        try {
+            final Canvas canvas = new Canvas(bitmap);
+            drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
+            drawable.draw(canvas);
+        } finally {
+            bitmap.recycle();
+        }
+        return renderBitmapToByteArray(bitmap);
+    }
+
+    private byte[] renderBitmapToByteArray(Bitmap bitmap) {
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream(bitmap.getByteCount());
+        bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
+        return baos.toByteArray();
+    }
+
+    /**
+     * Update the mute state of this call. No-op if the call is not capable of being muted.
+     *
+     * @param isMuted true if the call should be muted, and false if the call should be unmuted.
+     */
+    public void updateMuted(boolean isMuted) {
+        mIsMuted = isMuted;
+        updateCallDetails(mCall.getDetails());
+    }
+
+    /**
+     * Update the state of the call to be ringing silently if it is currently ringing. No-op if the
+     * call is not
+     * currently ringing.
+     */
+    public void updateSilencedIfRinging() {
+        if (mStatus == android.companion.Telecom.Call.RINGING) {
+            mStatus = android.companion.Telecom.Call.RINGING_SILENCED;
+        }
+        mControls.remove(android.companion.Telecom.Call.SILENCE);
+    }
+
+    @VisibleForTesting
+    void updateCallDetails(Call.Details callDetails) {
+        mCallerDisplayName = callDetails.getCallerDisplayName();
+        mContactDisplayName = callDetails.getContactDisplayName();
+        mStatus = convertStateToStatus(callDetails.getState());
+        mControls.clear();
+        if (mStatus == android.companion.Telecom.Call.RINGING
+                || mStatus == android.companion.Telecom.Call.RINGING_SILENCED) {
+            mControls.add(android.companion.Telecom.Call.ACCEPT);
+            mControls.add(android.companion.Telecom.Call.REJECT);
+            if (mStatus == android.companion.Telecom.Call.RINGING) {
+                mControls.add(android.companion.Telecom.Call.SILENCE);
+            }
+        }
+        if (mStatus == android.companion.Telecom.Call.ONGOING
+                || mStatus == android.companion.Telecom.Call.ON_HOLD) {
+            mControls.add(android.companion.Telecom.Call.END);
+            if (callDetails.can(Call.Details.CAPABILITY_HOLD)) {
+                mControls.add(
+                        mStatus == android.companion.Telecom.Call.ON_HOLD
+                                ? android.companion.Telecom.Call.TAKE_OFF_HOLD
+                                : android.companion.Telecom.Call.PUT_ON_HOLD);
+            }
+        }
+        if (mStatus == android.companion.Telecom.Call.ONGOING && callDetails.can(
+                Call.Details.CAPABILITY_MUTE)) {
+            mControls.add(mIsMuted ? android.companion.Telecom.Call.UNMUTE
+                    : android.companion.Telecom.Call.MUTE);
+        }
+    }
+
+    private int convertStateToStatus(int callState) {
+        switch (callState) {
+            case Call.STATE_HOLDING:
+                return android.companion.Telecom.Call.ON_HOLD;
+            case Call.STATE_ACTIVE:
+                return android.companion.Telecom.Call.ONGOING;
+            case Call.STATE_RINGING:
+                return android.companion.Telecom.Call.RINGING;
+            case Call.STATE_NEW:
+            case Call.STATE_DIALING:
+            case Call.STATE_DISCONNECTED:
+            case Call.STATE_SELECT_PHONE_ACCOUNT:
+            case Call.STATE_CONNECTING:
+            case Call.STATE_DISCONNECTING:
+            case Call.STATE_PULLING_CALL:
+            case Call.STATE_AUDIO_PROCESSING:
+            case Call.STATE_SIMULATED_RINGING:
+            default:
+                return android.companion.Telecom.Call.UNKNOWN_STATUS;
+        }
+    }
+
+    public long getId() {
+        return mId;
+    }
+
+    public Call getCall() {
+        return mCall;
+    }
+
+    public String getCallingAppName() {
+        return mCallingAppName;
+    }
+
+    public byte[] getCallingAppIcon() {
+        return mCallingAppIcon;
+    }
+
+    /**
+     * Get a human-readable "caller id" to display as the origin of the call.
+     *
+     * @param isAdminBlocked whether there is an admin that has blocked contacts over Bluetooth
+     */
+    public String getReadableCallerId(boolean isAdminBlocked) {
+        if (mIsOtt) {
+            return mCallerDisplayName;
+        }
+        return mIsEnterprise && isAdminBlocked ? mCallerDisplayName : mContactDisplayName;
+    }
+
+    public int getStatus() {
+        return mStatus;
+    }
+
+    public Set<Integer> getControls() {
+        return mControls;
+    }
+
+    void doAccept() {
+        mCall.answer(VideoProfile.STATE_AUDIO_ONLY);
+    }
+
+    void doReject() {
+        if (mStatus == android.companion.Telecom.Call.RINGING) {
+            mCall.reject(Call.REJECT_REASON_DECLINED);
+        }
+    }
+
+    void doEnd() {
+        mCall.disconnect();
+    }
+
+    void doPutOnHold() {
+        mCall.hold();
+    }
+
+    void doTakeOffHold() {
+        mCall.unhold();
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 47ec80e..2395814c 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -176,6 +176,11 @@
     @VisibleForTesting
     void notifyRunningAppsChanged(int deviceId, ArraySet<Integer> uids) {
         synchronized (mVirtualDeviceManagerLock) {
+            if (!mVirtualDevices.contains(deviceId)) {
+                Slog.e(TAG, "notifyRunningAppsChanged called for unknown deviceId:" + deviceId
+                        + " (maybe it was recently closed?)");
+                return;
+            }
             mAppsOnVirtualDevices.put(deviceId, uids);
         }
         mLocalService.onAppsOnVirtualDeviceChanged();
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index a35aa7c..70eeb7f 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -329,11 +329,11 @@
      * when the user is first unlocked to update the usage stats package mappings data that might
      * be stale or have existed from a restore and belongs to packages that are not installed for
      * this user anymore.
-     * Note: this is only executed for the system user.
      *
+     * @param userId The user to update
      * @return {@code true} if the updating was successful, {@code false} otherwise
      */
-    public abstract boolean updatePackageMappingsData();
+    public abstract boolean updatePackageMappingsData(@UserIdInt int userId);
 
     /**
      * Listener interface for usage events.
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index ff75796..4854c37 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -321,6 +321,7 @@
     private ArrayMap<String, Set<String>> mPackageToUserTypeBlacklist = new ArrayMap<>();
 
     private final ArraySet<String> mRollbackWhitelistedPackages = new ArraySet<>();
+    private final ArraySet<String> mAutomaticRollbackDenylistedPackages = new ArraySet<>();
     private final ArraySet<String> mWhitelistedStagedInstallers = new ArraySet<>();
     // A map from package name of vendor APEXes that can be updated to an installer package name
     // allowed to install updates for it.
@@ -461,6 +462,10 @@
         return mRollbackWhitelistedPackages;
     }
 
+    public Set<String> getAutomaticRollbackDenylistedPackages() {
+        return mAutomaticRollbackDenylistedPackages;
+    }
+
     public Set<String> getWhitelistedStagedInstallers() {
         return mWhitelistedStagedInstallers;
     }
@@ -1356,6 +1361,16 @@
                         }
                         XmlUtils.skipCurrentTag(parser);
                     } break;
+                    case "automatic-rollback-denylisted-app": {
+                        String pkgname = parser.getAttributeValue(null, "package");
+                        if (pkgname == null) {
+                            Slog.w(TAG, "<" + name + "> without package in " + permFile
+                                    + " at " + parser.getPositionDescription());
+                        } else {
+                            mAutomaticRollbackDenylistedPackages.add(pkgname);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
                     case "whitelisted-staged-installer": {
                         if (allowAppConfigs) {
                             String pkgname = parser.getAttributeValue(null, "package");
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 7b8ca91..4504f91 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2250,14 +2250,19 @@
 
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
+                boolean preciseCallStateChanged = false;
                 mRingingCallState[phoneId] = ringingCallState;
                 mForegroundCallState[phoneId] = foregroundCallState;
                 mBackgroundCallState[phoneId] = backgroundCallState;
-                mPreciseCallState[phoneId] = new PreciseCallState(
+                PreciseCallState preciseCallState = new PreciseCallState(
                         ringingCallState, foregroundCallState,
                         backgroundCallState,
                         DisconnectCause.NOT_VALID,
                         PreciseDisconnectCause.NOT_VALID);
+                if (!preciseCallState.equals(mPreciseCallState[phoneId])) {
+                    preciseCallStateChanged = true;
+                    mPreciseCallState[phoneId] = preciseCallState;
+                }
                 boolean notifyCallState = true;
                 if (mCallQuality == null) {
                     log("notifyPreciseCallState: mCallQuality is null, "
@@ -2271,6 +2276,8 @@
                         mCallNetworkType[phoneId] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
                         mCallQuality[phoneId] = createCallQuality();
                     }
+                    List<CallState> prevCallStateList = new ArrayList<>();
+                    prevCallStateList.addAll(mCallStateLists.get(phoneId));
                     mCallStateLists.get(phoneId).clear();
                     if (foregroundCallState != PreciseCallState.PRECISE_CALL_STATE_NOT_VALID
                             && foregroundCallState != PreciseCallState.PRECISE_CALL_STATE_IDLE) {
@@ -2330,6 +2337,9 @@
                         }
                         mCallStateLists.get(phoneId).add(builder.build());
                     }
+                    if (prevCallStateList.equals(mCallStateLists.get(phoneId))) {
+                        notifyCallState = false;
+                    }
                     boolean hasOngoingCall = false;
                     for (CallState cs : mCallStateLists.get(phoneId)) {
                         if (cs.getCallState() != PreciseCallState.PRECISE_CALL_STATE_DISCONNECTED) {
@@ -2343,23 +2353,30 @@
                     }
                 }
 
-                for (Record r : mRecords) {
-                    if (r.matchTelephonyCallbackEvent(
-                            TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)
-                            && idMatch(r, subId, phoneId)) {
-                        try {
-                            r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
-                        } catch (RemoteException ex) {
-                            mRemoveList.add(r.binder);
+                if (preciseCallStateChanged) {
+                    for (Record r : mRecords) {
+                        if (r.matchTelephonyCallbackEvent(
+                                TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)
+                                && idMatch(r, subId, phoneId)) {
+                            try {
+                                r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
+                            } catch (RemoteException ex) {
+                                mRemoveList.add(r.binder);
+                            }
                         }
                     }
-                    if (notifyCallState && r.matchTelephonyCallbackEvent(
-                            TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
-                            && idMatch(r, subId, phoneId)) {
-                        try {
-                            r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
-                        } catch (RemoteException ex) {
-                            mRemoveList.add(r.binder);
+                }
+
+                if (notifyCallState) {
+                    for (Record r : mRecords) {
+                        if (r.matchTelephonyCallbackEvent(
+                                TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
+                                && idMatch(r, subId, phoneId)) {
+                            try {
+                                r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
+                            } catch (RemoteException ex) {
+                                mRemoveList.add(r.binder);
+                            }
                         }
                     }
                 }
@@ -3976,66 +3993,6 @@
         }
     }
 
-    /**
-     * Returns a string representation of the radio technology (network type)
-     * currently in use on the device.
-     * @param type for which network type is returned
-     * @return the name of the radio technology
-     *
-     */
-    private String getNetworkTypeName(@Annotation.NetworkType int type) {
-        switch (type) {
-            case TelephonyManager.NETWORK_TYPE_GPRS:
-                return "GPRS";
-            case TelephonyManager.NETWORK_TYPE_EDGE:
-                return "EDGE";
-            case TelephonyManager.NETWORK_TYPE_UMTS:
-                return "UMTS";
-            case TelephonyManager.NETWORK_TYPE_HSDPA:
-                return "HSDPA";
-            case TelephonyManager.NETWORK_TYPE_HSUPA:
-                return "HSUPA";
-            case TelephonyManager.NETWORK_TYPE_HSPA:
-                return "HSPA";
-            case TelephonyManager.NETWORK_TYPE_CDMA:
-                return "CDMA";
-            case TelephonyManager.NETWORK_TYPE_EVDO_0:
-                return "CDMA - EvDo rev. 0";
-            case TelephonyManager.NETWORK_TYPE_EVDO_A:
-                return "CDMA - EvDo rev. A";
-            case TelephonyManager.NETWORK_TYPE_EVDO_B:
-                return "CDMA - EvDo rev. B";
-            case TelephonyManager.NETWORK_TYPE_1xRTT:
-                return "CDMA - 1xRTT";
-            case TelephonyManager.NETWORK_TYPE_LTE:
-                return "LTE";
-            case TelephonyManager.NETWORK_TYPE_EHRPD:
-                return "CDMA - eHRPD";
-            case TelephonyManager.NETWORK_TYPE_IDEN:
-                return "iDEN";
-            case TelephonyManager.NETWORK_TYPE_HSPAP:
-                return "HSPA+";
-            case TelephonyManager.NETWORK_TYPE_GSM:
-                return "GSM";
-            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
-                return "TD_SCDMA";
-            case TelephonyManager.NETWORK_TYPE_IWLAN:
-                return "IWLAN";
-
-            //TODO: This network type is marked as hidden because it is not a
-            // true network type and we are looking to remove it completely from the available list
-            // of network types.  Since this method is only used for logging, in the event that this
-            // network type is selected, the log will read as "Unknown."
-            //case TelephonyManager.NETWORK_TYPE_LTE_CA:
-            //    return "LTE_CA";
-
-            case TelephonyManager.NETWORK_TYPE_NR:
-                return "NR";
-            default:
-                return "UNKNOWN";
-        }
-    }
-
     /** Returns a new PreciseCallState object with default values. */
     private static PreciseCallState createPreciseCallState() {
         return new PreciseCallState(PreciseCallState.PRECISE_CALL_STATE_NOT_VALID,
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 41437d1..73bb8d7 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -206,6 +206,7 @@
 import com.android.server.SystemService;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
 import com.android.server.am.LowMemDetector.MemFactor;
+import com.android.server.am.ServiceRecord.ShortFgsInfo;
 import com.android.server.pm.KnownPackages;
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.wm.ActivityServiceConnectionsHolder;
@@ -1985,7 +1986,7 @@
                             foregroundServiceType == FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
                     final boolean isOldTypeShortFgsAndTimedOut = r.shouldTriggerShortFgsTimeout();
 
-                    if (isOldTypeShortFgs || isNewTypeShortFgs) {
+                    if (r.isForeground && (isOldTypeShortFgs || isNewTypeShortFgs)) {
                         if (DEBUG_SHORT_SERVICE) {
                             Slog.i(TAG_SERVICE, String.format(
                                     "FGS type changing from %x%s to %x: %s",
@@ -2021,10 +2022,8 @@
                             } else {
                                 // FGS type is changing from SHORT_SERVICE to another type when
                                 // an app is allowed to start FGS, so this will succeed.
-                                // The timeout will stop -- we actually don't cancel the handler
-                                // events, but they'll be ignored if the service type is not
-                                // SHORT_SERVICE.
-                                // TODO(short-service) Let's actaully cancel the handler events.
+                                // The timeout will stop later, in
+                                // maybeUpdateShortFgsTrackingLocked().
                             }
                         } else {
                             // We catch this case later, in the
@@ -2226,7 +2225,7 @@
                     mAm.notifyPackageUse(r.serviceInfo.packageName,
                             PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
 
-                    maybeStartShortFgsTimeoutAndUpdateShortFgsInfoLocked(r,
+                    maybeUpdateShortFgsTrackingLocked(r,
                             extendShortServiceTimeout);
                 } else {
                     if (DEBUG_FOREGROUND_SERVICE) {
@@ -3022,11 +3021,17 @@
     }
 
     /**
-     * If {@code sr} is of a short-fgs, start a short-FGS timeout.
+     * Update a {@link ServiceRecord}'s {@link ShortFgsInfo} as needed, and also start
+     * a timeout as needed.
+     *
+     * If the {@link ServiceRecord} is not a short-FGS, then we'll stop the timeout and clear
+     * the {@link ShortFgsInfo}.
      */
-    private void maybeStartShortFgsTimeoutAndUpdateShortFgsInfoLocked(ServiceRecord sr,
+    private void maybeUpdateShortFgsTrackingLocked(ServiceRecord sr,
             boolean extendTimeout) {
         if (!sr.isShortFgs()) {
+            sr.clearShortFgsInfo(); // Just in case we have it.
+            unscheduleShortFgsTimeoutLocked(sr);
             return;
         }
         if (DEBUG_SHORT_SERVICE) {
@@ -3035,29 +3040,32 @@
 
         if (extendTimeout || !sr.hasShortFgsInfo()) {
             sr.setShortFgsInfo(SystemClock.uptimeMillis());
+
+            // We'll restart the timeout.
+            unscheduleShortFgsTimeoutLocked(sr);
+
+            final Message msg = mAm.mHandler.obtainMessage(
+                    ActivityManagerService.SERVICE_SHORT_FGS_TIMEOUT_MSG, sr);
+            mAm.mHandler.sendMessageAtTime(msg, sr.getShortFgsInfo().getTimeoutTime());
         } else {
             // We only (potentially) update the start command, start count, but not the timeout
             // time.
+            // In this case, we keep the existing timeout running.
             sr.getShortFgsInfo().update();
         }
-        unscheduleShortFgsTimeoutLocked(sr); // Do it just in case
-
-        final Message msg = mAm.mHandler.obtainMessage(
-                ActivityManagerService.SERVICE_SHORT_FGS_TIMEOUT_MSG, sr);
-        mAm.mHandler.sendMessageAtTime(msg, sr.getShortFgsInfo().getTimeoutTime());
     }
 
     /**
      * Stop the timeout for a ServiceRecord, if it's of a short-FGS.
      */
     private void maybeStopShortFgsTimeoutLocked(ServiceRecord sr) {
+        sr.clearShortFgsInfo(); // Always clear, just in case.
         if (!sr.isShortFgs()) {
             return;
         }
         if (DEBUG_SHORT_SERVICE) {
             Slog.i(TAG_SERVICE, "Stop short FGS timeout: " + sr);
         }
-        sr.clearShortFgsInfo();
         unscheduleShortFgsTimeoutLocked(sr);
     }
 
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index f54e2b0..410bc41 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -118,7 +118,7 @@
     private static final int FREEZE_BINDER_TIMEOUT_MS = 100;
 
     // Defaults for phenotype flags.
-    @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
+    @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = true;
     @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
     @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_ALL;
     @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE;
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 85de637..8d3c21e 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -57,6 +57,7 @@
 import android.content.pm.PathPermission;
 import android.content.pm.ProviderInfo;
 import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
@@ -87,7 +88,6 @@
 import com.android.server.LocalServices;
 import com.android.server.RescueParty;
 import com.android.server.pm.UserManagerInternal;
-import com.android.server.pm.UserManagerService;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.sdksandbox.SdkSandboxManagerLocal;
 
@@ -186,7 +186,7 @@
 
             checkTime(startTime, "getContentProviderImpl: getProviderByName");
 
-            UserManagerService userManagerService = UserManagerService.getInstance();
+            UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
 
             /*
              For clone user profile and allowed authority, skipping finding provider and redirecting
@@ -196,8 +196,10 @@
              used and redirect to owner user's MediaProvider.
              */
             //todo(b/236121588) MediaProvider should not be installed in clone profile.
-            if (!isAuthorityRedirectedForCloneProfile(name)
-                    || !userManagerService.isMediaSharedWithParent(userId)) {
+            final UserProperties userProps = umInternal.getUserProperties(userId);
+            final boolean isMediaSharedWithParent =
+                    userProps != null && userProps.isMediaSharedWithParent();
+            if (!isAuthorityRedirectedForCloneProfile(name) || !isMediaSharedWithParent) {
                 // First check if this content provider has been published...
                 cpr = mProviderMap.getProviderByName(name, userId);
             }
@@ -215,9 +217,7 @@
                         userId = UserHandle.USER_SYSTEM;
                         checkCrossUser = false;
                     } else if (isAuthorityRedirectedForCloneProfile(name)) {
-                        if (userManagerService.isMediaSharedWithParent(userId)) {
-                            UserManagerInternal umInternal = LocalServices.getService(
-                                    UserManagerInternal.class);
+                        if (isMediaSharedWithParent) {
                             userId = umInternal.getProfileParentId(userId);
                             checkCrossUser = false;
                         }
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 43075bc..14ecf9f 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -32,6 +32,7 @@
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.Overridable;
 import android.content.IIntentReceiver;
 import android.content.IIntentSender;
 import android.content.Intent;
@@ -65,6 +66,7 @@
     /** If enabled BAL are prevented by default in applications targeting U and later. */
     @ChangeId
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    @Overridable
     private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER = 244637991;
     private static final String ENABLE_DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER =
             "enable_default_rescind_bal_privileges_from_pending_intent_sender";
@@ -450,16 +452,11 @@
                 resolvedType = key.requestResolvedType;
             }
 
-            // Apply any launch flags from the ActivityOptions. This is used only by SystemUI
-            // to ensure that we can launch the pending intent with a consistent launch mode even
-            // if the provided PendingIntent is immutable (ie. to force an activity to launch into
-            // a new task, or to launch multiple instances if supported by the app)
+            // Apply any launch flags from the ActivityOptions. This is to ensure that the caller
+            // can specify a consistent launch mode even if the PendingIntent is immutable
             final ActivityOptions opts = ActivityOptions.fromBundle(options);
             if (opts != null) {
-                // TODO(b/254490217): Move this check into SafeActivityOptions
-                if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) {
-                    finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
-                }
+                finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
             }
 
             // Extract options before clearing calling identity
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 9bb63d3..58a47d7 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -49,6 +49,7 @@
 import com.android.internal.annotations.CompositeRWLock;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.expresslog.Counter;
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TimeoutRecord;
 import com.android.internal.os.anr.AnrLatencyTracker;
@@ -260,6 +261,28 @@
         mDialogController = new ErrorDialogController(app);
     }
 
+    @GuardedBy("mService")
+    boolean skipAnrLocked(String annotation) {
+        // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
+        if (mService.mAtmInternal.isShuttingDown()) {
+            Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
+            return true;
+        } else if (isNotResponding()) {
+            Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
+            return true;
+        } else if (isCrashing()) {
+            Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
+            return true;
+        } else if (mApp.isKilledByAm()) {
+            Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
+            return true;
+        } else if (mApp.isKilled()) {
+            Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
+            return true;
+        }
+        return false;
+    }
+
     void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
             String parentShortComponentName, WindowProcessController parentProcess,
             boolean aboveSystem, TimeoutRecord timeoutRecord,
@@ -303,26 +326,10 @@
             // Store annotation here as instance above will not be hit on all paths.
             setAnrAnnotation(annotation);
 
-            // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
-            if (mService.mAtmInternal.isShuttingDown()) {
-                Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
+            Counter.logIncrement("stability_anr.value_total_anrs");
+            if (skipAnrLocked(annotation)) {
                 latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
-                return;
-            } else if (isNotResponding()) {
-                Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
-                latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
-                return;
-            } else if (isCrashing()) {
-                Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
-                latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
-                return;
-            } else if (mApp.isKilledByAm()) {
-                Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
-                latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
-                return;
-            } else if (mApp.isKilled()) {
-                Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
-                latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
+                Counter.logIncrement("stability_anr.value_skipped_anrs");
                 return;
             }
 
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 67166b8..8ce9889 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1457,8 +1457,7 @@
     }
 
     private boolean shouldStartWithParent(UserInfo user) {
-        final UserProperties properties = mInjector.getUserManagerInternal()
-                .getUserProperties(user.id);
+        final UserProperties properties = getUserProperties(user.id);
         return (properties != null && properties.getStartWithParent())
                 && !user.isQuietModeEnabled();
     }
@@ -2773,6 +2772,10 @@
         return mInjector.getUserManager().getUserInfo(userId);
     }
 
+    private @Nullable UserProperties getUserProperties(@UserIdInt int userId) {
+        return mInjector.getUserManagerInternal().getUserProperties(userId);
+    }
+
     int[] getUserIds() {
         return mInjector.getUserManager().getUserIds();
     }
@@ -2903,7 +2906,8 @@
         if (getStartedUserState(userId) == null) {
             return false;
         }
-        if (!mInjector.getUserManager().isCredentialSharableWithParent(userId)) {
+        final UserProperties properties = getUserProperties(userId);
+        if (properties == null || !properties.isCredentialShareableWithParent()) {
             return false;
         }
         if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
index b18be3c..08dcccf 100644
--- a/services/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -86,7 +86,7 @@
 
     private static final Set<String> sEligibleForMultiUser = Sets.newArraySet(
             PERMISSION_HELPER, NOTIFICATION_HELPER, SYNC_SETTINGS_HELPER, APP_LOCALES_HELPER,
-            ACCOUNT_MANAGER_HELPER);
+            ACCOUNT_MANAGER_HELPER, USAGE_STATS_HELPER);
 
     private int mUserId = UserHandle.USER_SYSTEM;
 
@@ -100,7 +100,7 @@
         addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(mUserId));
         addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(mUserId));
         addHelper(PERMISSION_HELPER, new PermissionBackupHelper(mUserId));
-        addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
+        addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(mUserId));
         addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
         addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper(mUserId));
         addHelper(SLICES_HELPER, new SliceBackupHelper(this));
diff --git a/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java b/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java
index d6a70d3..4098c1a 100644
--- a/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java
+++ b/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java
@@ -1,9 +1,9 @@
 package com.android.server.backup;
 
 
+import android.annotation.UserIdInt;
 import android.app.backup.BlobBackupHelper;
 import android.app.usage.UsageStatsManagerInternal;
-import android.content.Context;
 import android.os.UserHandle;
 import android.util.Log;
 
@@ -26,8 +26,16 @@
     // same as UsageStatsDatabase.KEY_USAGE_STATS
     static final String KEY_USAGE_STATS = "usage_stats";
 
-    public UsageStatsBackupHelper(Context context) {
+    private final @UserIdInt int mUserId;
+
+    /**
+     * Marshall/unmarshall the usagestats data for the given user
+     *
+     * @param userId The userId to backup/restore
+     */
+    public UsageStatsBackupHelper(@UserIdInt int userId) {
         super(BLOB_VERSION, KEY_USAGE_STATS);
+        mUserId = userId;
     }
 
     @Override
@@ -38,8 +46,11 @@
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             DataOutputStream out  = new DataOutputStream(baos);
             try {
+                // Note: Write 0 here deliberately so that a backup from a secondary user
+                // can still be restored to an older OS where the restore was always to user 0
+                // Writing the actual userId here would result in restores not working on pre-U.
                 out.writeInt(UserHandle.USER_SYSTEM);
-                out.write(localUsageStatsManager.getBackupPayload(UserHandle.USER_SYSTEM, key));
+                out.write(localUsageStatsManager.getBackupPayload(mUserId, key));
             } catch (IOException ioe) {
                 if (DEBUG) Log.e(TAG, "Failed to backup Usage Stats", ioe);
                 baos.reset();
@@ -49,7 +60,6 @@
         return null;
     }
 
-
     @Override
     protected void applyRestoredPayload(String key, byte[] payload)  {
         if (KEY_USAGE_STATS.equals(key)) {
@@ -57,10 +67,10 @@
                     LocalServices.getService(UsageStatsManagerInternal.class);
             DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
             try {
-                int user = in.readInt();
+                in.readInt(); // Legacy userId parameter, read and ignore
                 byte[] restoreData = new byte[payload.length - 4];
                 in.read(restoreData, 0, restoreData.length);
-                localUsageStatsManager.applyRestoredPayload(user, key, restoreData);
+                localUsageStatsManager.applyRestoredPayload(mUserId, key, restoreData);
             } catch (IOException ioe) {
                 if (DEBUG) Log.e(TAG, "Failed to restore Usage Stats", ioe);
             }
diff --git a/services/core/java/com/android/server/display/BrightnessSetting.java b/services/core/java/com/android/server/display/BrightnessSetting.java
index 7448611..4a9b562 100644
--- a/services/core/java/com/android/server/display/BrightnessSetting.java
+++ b/services/core/java/com/android/server/display/BrightnessSetting.java
@@ -96,7 +96,11 @@
         mListeners.remove(l);
     }
 
-    void setBrightness(float brightness) {
+    /**
+     * Sets the brigtness and broadcasts the change to the listeners.
+     * @param brightness The value to which the brightness is to be set.
+     */
+    public void setBrightness(float brightness) {
         if (Float.isNaN(brightness)) {
             Slog.w(TAG, "Attempting to set invalid brightness");
             return;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d558e69..466070f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -109,6 +109,7 @@
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.IntArray;
@@ -260,6 +261,13 @@
     final SparseArray<Pair<IVirtualDevice, DisplayWindowPolicyController>>
             mDisplayWindowPolicyControllers = new SparseArray<>();
 
+    /**
+     *  Map of every internal primary display device {@link HighBrightnessModeMetadata}s indexed by
+     *  {@link DisplayDevice#mUniqueId}.
+     */
+    public final ArrayMap<String, HighBrightnessModeMetadata> mHighBrightnessModeMetadataMap =
+            new ArrayMap<>();
+
     // List of all currently registered display adapters.
     private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
 
@@ -1640,7 +1648,16 @@
 
         DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
         if (dpc != null) {
-            dpc.onDisplayChanged();
+            final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+            if (device == null) {
+                Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
+                        + display.getDisplayIdLocked());
+                return;
+            }
+
+            final String uniqueId = device.getUniqueId();
+            HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
+            dpc.onDisplayChanged(hbmMetadata);
         }
     }
 
@@ -1698,7 +1715,15 @@
         final int displayId = display.getDisplayIdLocked();
         final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
         if (dpc != null) {
-            dpc.onDisplayChanged();
+            final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+            if (device == null) {
+                Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
+                        + display.getDisplayIdLocked());
+                return;
+            }
+            final String uniqueId = device.getUniqueId();
+            HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
+            dpc.onDisplayChanged(hbmMetadata);
         }
     }
 
@@ -2651,6 +2676,26 @@
         mLogicalDisplayMapper.forEachLocked(this::addDisplayPowerControllerLocked);
     }
 
+    private HighBrightnessModeMetadata getHighBrightnessModeMetadata(LogicalDisplay display) {
+        final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+        if (device == null) {
+            Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: "
+                    + display.getDisplayIdLocked());
+            return null;
+        }
+
+        final String uniqueId = device.getUniqueId();
+
+        if (mHighBrightnessModeMetadataMap.containsKey(uniqueId)) {
+            return mHighBrightnessModeMetadataMap.get(uniqueId);
+        }
+
+        // HBM Time info not present. Create a new one for this physical display.
+        HighBrightnessModeMetadata hbmInfo = new HighBrightnessModeMetadata();
+        mHighBrightnessModeMetadataMap.put(uniqueId, hbmInfo);
+        return hbmInfo;
+    }
+
     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
     private void addDisplayPowerControllerLocked(LogicalDisplay display) {
         if (mPowerHandler == null) {
@@ -2666,17 +2711,23 @@
                 display, mSyncRoot);
         final DisplayPowerControllerInterface displayPowerController;
 
+        // If display is internal and has a HighBrightnessModeMetadata mapping, use that.
+        // Or create a new one and use that.
+        // We also need to pass a mapping of the HighBrightnessModeTimeInfoMap to
+        // displayPowerController, so the hbm info can be correctly associated
+        // with the corresponding displaydevice.
+        HighBrightnessModeMetadata hbmMetadata = getHighBrightnessModeMetadata(display);
         if (DeviceConfig.getBoolean("display_manager",
                 "use_newly_structured_display_power_controller", true)) {
             displayPowerController = new DisplayPowerController2(
                     mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
                     mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                    () -> handleBrightnessChange(display));
+                    () -> handleBrightnessChange(display), hbmMetadata);
         } else {
             displayPowerController = new DisplayPowerController(
                     mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
                     mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                    () -> handleBrightnessChange(display));
+                    () -> handleBrightnessChange(display), hbmMetadata);
         }
         mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
     }
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index c960416..f8d6c5f 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -3081,10 +3081,10 @@
 
         @Override
         public boolean supportsFrameRateOverride() {
-            return SurfaceFlingerProperties.enable_frame_rate_override().orElse(true)
+            return SurfaceFlingerProperties.enable_frame_rate_override().orElse(false)
                             && !SurfaceFlingerProperties.frame_rate_override_for_native_rates()
-                                    .orElse(false)
-                            && SurfaceFlingerProperties.frame_rate_override_global().orElse(true);
+                                    .orElse(true)
+                            && SurfaceFlingerProperties.frame_rate_override_global().orElse(false);
         }
 
         private DisplayManager getDisplayManager() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index cdaa3d0..142ec68 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -388,6 +388,7 @@
     private float[] mNitsRange;
 
     private final HighBrightnessModeController mHbmController;
+    private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;
 
     private final BrightnessThrottler mBrightnessThrottler;
 
@@ -505,13 +506,14 @@
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
-            Runnable onBrightnessChangeRunnable) {
+            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
 
         mInjector = injector != null ? injector : new Injector();
         mClock = mInjector.getClock();
         mLogicalDisplay = logicalDisplay;
         mDisplayId = mLogicalDisplay.getDisplayIdLocked();
         mTag = "DisplayPowerController[" + mDisplayId + "]";
+        mHighBrightnessModeMetadata = hbmMetadata;
         mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
         mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
         mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
@@ -790,7 +792,7 @@
      * Make sure DisplayManagerService.mSyncRoot is held when this is called
      */
     @Override
-    public void onDisplayChanged() {
+    public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata) {
         final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         if (device == null) {
             Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: "
@@ -812,7 +814,7 @@
                 mUniqueDisplayId = uniqueId;
                 mDisplayStatsId = mUniqueDisplayId.hashCode();
                 mDisplayDeviceConfig = config;
-                loadFromDisplayDeviceConfig(token, info);
+                loadFromDisplayDeviceConfig(token, info, hbmMetadata);
 
                 /// Since the underlying display-device changed, we really don't know the
                 // last command that was sent to change it's state. Lets assume it is unknown so
@@ -864,7 +866,8 @@
         }
     }
 
-    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
+    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
+                                             HighBrightnessModeMetadata hbmMetadata) {
         // All properties that depend on the associated DisplayDevice and the DDC must be
         // updated here.
         loadBrightnessRampRates();
@@ -877,6 +880,7 @@
                     mBrightnessRampIncreaseMaxTimeMillis,
                     mBrightnessRampDecreaseMaxTimeMillis);
         }
+        mHbmController.setHighBrightnessModeMetadata(hbmMetadata);
         mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
                 mDisplayDeviceConfig.getHighBrightnessModeData(),
                 new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -1961,7 +1965,7 @@
                     if (mAutomaticBrightnessController != null) {
                         mAutomaticBrightnessController.update();
                     }
-                }, mContext);
+                }, mHighBrightnessModeMetadata, mContext);
     }
 
     private BrightnessThrottler createBrightnessThrottlerLocked() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index da59cca..ba9fe38 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -68,6 +68,7 @@
 import com.android.server.display.RampAnimator.DualRampAnimator;
 import com.android.server.display.brightness.BrightnessEvent;
 import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.BrightnessUtils;
 import com.android.server.display.brightness.DisplayBrightnessController;
 import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
 import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
@@ -197,8 +198,6 @@
     // mScreenBrightnessDimConfig.
     private final float mScreenBrightnessMinimumDimAmount;
 
-    private final float mScreenBrightnessDefault;
-
     // True if auto-brightness should be used.
     private boolean mUseSoftwareAutoBrightnessConfig;
 
@@ -328,11 +327,10 @@
     private float[] mNitsRange;
 
     private final HighBrightnessModeController mHbmController;
+    private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;
 
     private final BrightnessThrottler mBrightnessThrottler;
 
-    private final BrightnessSetting mBrightnessSetting;
-
     private final Runnable mOnBrightnessChangeRunnable;
 
     private final BrightnessEvent mLastBrightnessEvent;
@@ -383,19 +381,6 @@
     @Nullable
     private BrightnessConfiguration mBrightnessConfiguration;
 
-    // The last brightness that was set by the user and not temporary. Set to
-    // PowerManager.BRIGHTNESS_INVALID_FLOAT when a brightness has yet to be recorded.
-    private float mLastUserSetScreenBrightness = Float.NaN;
-
-    // The screen brightness setting has changed but not taken effect yet. If this is different
-    // from the current screen brightness setting then this is coming from something other than us
-    // and should be considered a user interaction.
-    private float mPendingScreenBrightnessSetting;
-
-    // The last observed screen brightness setting, either set by us or by the settings app on
-    // behalf of the user.
-    private float mCurrentScreenBrightnessSetting;
-
     // The last auto brightness adjustment that was set by the user and not temporary. Set to
     // Float.NaN when an auto-brightness adjustment hasn't been recorded yet.
     private float mAutoBrightnessAdjustment;
@@ -416,7 +401,6 @@
     private ObjectAnimator mColorFadeOnAnimator;
     private ObjectAnimator mColorFadeOffAnimator;
     private DualRampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
-    private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener;
 
     // True if this DisplayPowerController2 has been stopped and should no longer be running.
     private boolean mStopped;
@@ -432,7 +416,7 @@
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
-            Runnable onBrightnessChangeRunnable) {
+            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
 
         mInjector = injector != null ? injector : new Injector();
         mClock = mInjector.getClock();
@@ -448,6 +432,7 @@
         mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
                 mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
                 () -> updatePowerState(), mDisplayId, mSensorManager);
+        mHighBrightnessModeMetadata = hbmMetadata;
         mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController);
         mTag = "DisplayPowerController2[" + mDisplayId + "]";
 
@@ -469,8 +454,6 @@
         mBlanker = blanker;
         mContext = context;
         mBrightnessTracker = brightnessTracker;
-        // TODO: b/186428377 update brightness setting when display changes
-        mBrightnessSetting = brightnessSetting;
         mOnBrightnessChangeRunnable = onBrightnessChangeRunnable;
 
         PowerManager pm = context.getSystemService(PowerManager.class);
@@ -478,18 +461,13 @@
         final Resources resources = context.getResources();
 
         // DOZE AND DIM SETTINGS
-        mScreenBrightnessDozeConfig = clampAbsoluteBrightness(
+        mScreenBrightnessDozeConfig = BrightnessUtils.clampAbsoluteBrightness(
                 pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE));
-        mScreenBrightnessDimConfig = clampAbsoluteBrightness(
+        mScreenBrightnessDimConfig = BrightnessUtils.clampAbsoluteBrightness(
                 pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM));
         mScreenBrightnessMinimumDimAmount = resources.getFloat(
                 R.dimen.config_screenBrightnessMinimumDimAmountFloat);
 
-
-        // NORMAL SCREEN SETTINGS
-        mScreenBrightnessDefault = clampAbsoluteBrightness(
-                mLogicalDisplay.getDisplayInfoLocked().brightnessDefault);
-
         loadBrightnessRampRates();
         mSkipScreenOnBrightnessRamp = resources.getBoolean(
                 R.bool.config_skipScreenOnBrightnessRamp);
@@ -497,7 +475,10 @@
         mHbmController = createHbmControllerLocked();
 
         mBrightnessThrottler = createBrightnessThrottlerLocked();
-
+        mDisplayBrightnessController =
+                new DisplayBrightnessController(context, null,
+                        mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault,
+                        brightnessSetting, () -> postBrightnessChangeRunnable());
         // Seed the cached brightness
         saveBrightnessInfo(getScreenBrightnessSetting());
 
@@ -552,12 +533,7 @@
 
         mBrightnessBucketsInDozeConfig = resources.getBoolean(
                 R.bool.config_displayBrightnessBucketsInDoze);
-
-        mDisplayBrightnessController =
-                new DisplayBrightnessController(context, null, mDisplayId);
-        mCurrentScreenBrightnessSetting = getScreenBrightnessSetting();
         mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
-        mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
@@ -707,7 +683,7 @@
      * Make sure DisplayManagerService.mSyncRoot lock is held when this is called
      */
     @Override
-    public void onDisplayChanged() {
+    public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata) {
         final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         if (device == null) {
             Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: "
@@ -721,6 +697,7 @@
         final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
         final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
+
         mHandler.post(() -> {
             boolean changed = false;
             if (mDisplayDevice != device) {
@@ -729,7 +706,7 @@
                 mUniqueDisplayId = uniqueId;
                 mDisplayStatsId = mUniqueDisplayId.hashCode();
                 mDisplayDeviceConfig = config;
-                loadFromDisplayDeviceConfig(token, info);
+                loadFromDisplayDeviceConfig(token, info, hbmMetadata);
                 mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
 
                 // Since the underlying display-device changed, we really don't know the
@@ -770,15 +747,14 @@
                 mAutomaticBrightnessController.stop();
             }
 
-            if (mBrightnessSetting != null) {
-                mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
-            }
+            mDisplayBrightnessController.stop();
 
             mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
         }
     }
 
-    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
+    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
+                                             HighBrightnessModeMetadata hbmMetadata) {
         // All properties that depend on the associated DisplayDevice and the DDC must be
         // updated here.
         loadBrightnessRampRates();
@@ -790,6 +766,7 @@
                     mBrightnessRampIncreaseMaxTimeMillis,
                     mBrightnessRampDecreaseMaxTimeMillis);
         }
+        mHbmController.setHighBrightnessModeMetadata(hbmMetadata);
         mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
                 mDisplayDeviceConfig.getHighBrightnessModeData(),
                 new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -849,12 +826,14 @@
         if (mBrightnessTracker != null && brightness >= PowerManager.BRIGHTNESS_MIN) {
             mBrightnessTracker.start(brightness);
         }
-        mBrightnessSettingListener = brightnessValue -> {
+
+        BrightnessSetting.BrightnessSettingListener brightnessSettingListener = brightnessValue -> {
             Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue);
             mHandler.sendMessage(msg);
         };
+        mDisplayBrightnessController
+                .registerBrightnessSettingChangeListener(brightnessSettingListener);
 
-        mBrightnessSetting.registerListener(mBrightnessSettingListener);
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ),
                 false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
@@ -1204,7 +1183,8 @@
                         ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
                         : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
 
-        final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
+        final boolean userSetBrightnessChanged = mDisplayBrightnessController
+                .updateUserSetScreenBrightness();
 
         final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
 
@@ -1230,7 +1210,7 @@
             hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
             mAutomaticBrightnessController.configure(autoBrightnessState,
                     mBrightnessConfiguration,
-                    mLastUserSetScreenBrightness,
+                    mDisplayBrightnessController.getLastUserSetScreenBrightness(),
                     userSetBrightnessChanged, autoBrightnessAdjustment,
                     autoBrightnessAdjustmentChanged, mPowerRequest.policy,
                     mShouldResetShortTermModel);
@@ -1242,7 +1222,7 @@
         }
 
         boolean updateScreenBrightnessSetting = false;
-
+        float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness();
         // Apply auto-brightness.
         boolean slowChange = false;
         if (Float.isNaN(brightnessState)) {
@@ -1253,14 +1233,14 @@
                 newAutoBrightnessAdjustment =
                         mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment();
             }
-            if (isValidBrightnessValue(brightnessState)
+            if (BrightnessUtils.isValidBrightnessValue(brightnessState)
                     || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) {
                 // Use current auto-brightness value and slowly adjust to changes.
                 brightnessState = clampScreenBrightness(brightnessState);
                 if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
                     slowChange = true; // slowly adapt to auto-brightness
                 }
-                updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
+                updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState;
                 mAppliedAutoBrightness = true;
                 mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
                 if (mScreenOffBrightnessSensorController != null) {
@@ -1298,9 +1278,10 @@
         if (Float.isNaN(brightnessState) && autoBrightnessEnabled
                 && mScreenOffBrightnessSensorController != null) {
             brightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
-            if (isValidBrightnessValue(brightnessState)) {
+            if (BrightnessUtils.isValidBrightnessValue(brightnessState)) {
                 brightnessState = clampScreenBrightness(brightnessState);
-                updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
+                updateScreenBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness()
+                        != brightnessState;
                 mBrightnessReasonTemp.setReason(
                         BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR);
             }
@@ -1308,8 +1289,8 @@
 
         // Apply manual brightness.
         if (Float.isNaN(brightnessState)) {
-            brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
-            if (brightnessState != mCurrentScreenBrightnessSetting) {
+            brightnessState = clampScreenBrightness(currentBrightnessSetting);
+            if (brightnessState != currentBrightnessSetting) {
                 // The manually chosen screen brightness is outside of the currently allowed
                 // range (i.e., high-brightness-mode), make sure we tell the rest of the system
                 // by updating the setting.
@@ -1346,7 +1327,7 @@
             // before applying the low power or dim transformations so that the slider
             // accurately represents the full possible range, even if they range changes what
             // it means in absolute terms.
-            updateScreenBrightnessSetting(brightnessState);
+            mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessState);
         }
 
         // Apply dimming by at least some minimum amount when user activity
@@ -1457,7 +1438,7 @@
 
             final float currentBrightness = mPowerState.getScreenBrightness();
             final float currentSdrBrightness = mPowerState.getSdrScreenBrightness();
-            if (isValidBrightnessValue(animateValue)
+            if (BrightnessUtils.isValidBrightnessValue(animateValue)
                     && (animateValue != currentBrightness
                     || sdrAnimateValue != currentSdrBrightness)) {
                 if (initialRampSkip || hasBrightnessBuckets
@@ -1742,7 +1723,7 @@
                     if (mAutomaticBrightnessController != null) {
                         mAutomaticBrightnessController.update();
                     }
-                }, mContext);
+                }, mHighBrightnessModeMetadata, mContext);
     }
 
     private BrightnessThrottler createBrightnessThrottlerLocked() {
@@ -1892,12 +1873,6 @@
                 mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax());
     }
 
-    // Checks whether the brightness is within the valid brightness range, not including off.
-    private boolean isValidBrightnessValue(float brightness) {
-        return brightness >= PowerManager.BRIGHTNESS_MIN
-                && brightness <= PowerManager.BRIGHTNESS_MAX;
-    }
-
     private void animateScreenBrightness(float target, float sdrTarget, float rate) {
         if (DEBUG) {
             Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
@@ -2078,11 +2053,14 @@
     }
 
     private void handleSettingsChange(boolean userSwitch) {
-        mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
+        mDisplayBrightnessController
+                .setPendingScreenBrightness(mDisplayBrightnessController
+                        .getScreenBrightnessSetting());
         mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
         if (userSwitch) {
             // Don't treat user switches as user initiated change.
-            setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
+            mDisplayBrightnessController.setCurrentScreenBrightness(mDisplayBrightnessController
+                    .getPendingScreenBrightness());
             updateAutoBrightnessAdjustment();
             if (mAutomaticBrightnessController != null) {
                 mAutomaticBrightnessController.resetShortTermModel();
@@ -2111,34 +2089,12 @@
 
     @Override
     public float getScreenBrightnessSetting() {
-        float brightness = mBrightnessSetting.getBrightness();
-        if (Float.isNaN(brightness)) {
-            brightness = mScreenBrightnessDefault;
-        }
-        return clampAbsoluteBrightness(brightness);
+        return mDisplayBrightnessController.getScreenBrightnessSetting();
     }
 
     @Override
     public void setBrightness(float brightnessValue) {
-        // Update the setting, which will eventually call back into DPC to have us actually update
-        // the display with the new value.
-        mBrightnessSetting.setBrightness(brightnessValue);
-    }
-
-    private void updateScreenBrightnessSetting(float brightnessValue) {
-        if (!isValidBrightnessValue(brightnessValue)
-                || brightnessValue == mCurrentScreenBrightnessSetting) {
-            return;
-        }
-        setCurrentScreenBrightness(brightnessValue);
-        mBrightnessSetting.setBrightness(brightnessValue);
-    }
-
-    private void setCurrentScreenBrightness(float brightnessValue) {
-        if (brightnessValue != mCurrentScreenBrightnessSetting) {
-            mCurrentScreenBrightnessSetting = brightnessValue;
-            postBrightnessChangeRunnable();
-        }
+        mDisplayBrightnessController.setBrightness(brightnessValue);
     }
 
     private void putAutoBrightnessAdjustmentSetting(float adjustment) {
@@ -2164,28 +2120,6 @@
         return true;
     }
 
-    // We want to return true if the user has set the screen brightness.
-    // RBC on, off, and intensity changes will return false.
-    // Slider interactions whilst in RBC will return true, just as when in non-rbc.
-    private boolean updateUserSetScreenBrightness() {
-        if ((Float.isNaN(mPendingScreenBrightnessSetting)
-                || mPendingScreenBrightnessSetting < 0.0f)) {
-            return false;
-        }
-        if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
-            mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
-            mDisplayBrightnessController
-                    .setTemporaryBrightness(PowerManager.BRIGHTNESS_INVALID_FLOAT);
-            return false;
-        }
-        setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
-        mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
-        mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
-        mDisplayBrightnessController
-                .setTemporaryBrightness(PowerManager.BRIGHTNESS_INVALID_FLOAT);
-        return true;
-    }
-
     private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
             boolean hadUserDataPoint) {
         final float brightnessInNits = convertToNits(brightness);
@@ -2230,7 +2164,6 @@
 
         pw.println();
         pw.println("Display Power Controller Configuration:");
-        pw.println("  mScreenBrightnessRangeDefault=" + mScreenBrightnessDefault);
         pw.println("  mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig);
         pw.println("  mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
         pw.println("  mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
@@ -2261,9 +2194,6 @@
         pw.println();
         pw.println("Display Power Controller Thread State:");
         pw.println("  mPowerRequest=" + mPowerRequest);
-        pw.println("  mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness);
-        pw.println("  mPendingScreenBrightnessSetting="
-                + mPendingScreenBrightnessSetting);
         pw.println("  mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
         pw.println("  mBrightnessReason=" + mBrightnessReason);
         pw.println("  mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment);
@@ -2380,11 +2310,6 @@
         }
     }
 
-    private static float clampAbsoluteBrightness(float value) {
-        return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN,
-                PowerManager.BRIGHTNESS_MAX);
-    }
-
     private static float clampAutoBrightnessAdjustment(float value) {
         return MathUtils.constrain(value, -1.0f, 1.0f);
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index e750ee2..7b019846 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -31,10 +31,14 @@
 public interface DisplayPowerControllerInterface {
 
     /**
-     * Notified when the display is changed. We use this to apply any changes that might be needed
+     * Notified when the display is changed.
+     * We use this to apply any changes that might be needed
      * when displays get swapped on foldable devices.
+     * We also pass the High brightness mode metadata like
+     * remaining time and hbm events for the corresponding
+     * physical display, to update the values correctly.
      */
-    void onDisplayChanged();
+    void onDisplayChanged(HighBrightnessModeMetadata hbmInfo);
 
     /**
      * Unregisters all listeners and interrupts all running threads; halting future work.
diff --git a/services/core/java/com/android/server/display/HbmEvent.java b/services/core/java/com/android/server/display/HbmEvent.java
new file mode 100644
index 0000000..5675e2f
--- /dev/null
+++ b/services/core/java/com/android/server/display/HbmEvent.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+
+/**
+ * Represents an event in which High Brightness Mode was enabled.
+ */
+class HbmEvent {
+    private long mStartTimeMillis;
+    private long mEndTimeMillis;
+
+    HbmEvent(long startTimeMillis, long endTimeMillis) {
+        this.mStartTimeMillis = startTimeMillis;
+        this.mEndTimeMillis = endTimeMillis;
+    }
+
+    public long getStartTimeMillis() {
+        return mStartTimeMillis;
+    }
+
+    public long getEndTimeMillis() {
+        return mEndTimeMillis;
+    }
+
+    @Override
+    public String toString() {
+        return "HbmEvent: {startTimeMillis:" + mStartTimeMillis + ", endTimeMillis: "
+                + mEndTimeMillis + "}, total: "
+                + ((mEndTimeMillis - mStartTimeMillis) / 1000) + "]";
+    }
+}
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index f98c7df..2c843a4 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -105,30 +105,23 @@
     private int mHbmStatsState = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF;
 
     /**
-     * If HBM is currently running, this is the start time for the current HBM session.
+     * If HBM is currently running, this is the start time and set of all events,
+     * for the current HBM session.
      */
-    private long mRunningStartTimeMillis = -1;
-
-    /**
-     * Queue of previous HBM-events ordered from most recent to least recent.
-     * Meant to store only the events that fall into the most recent
-     * {@link HighBrightnessModeData#timeWindowMillis mHbmData.timeWindowMillis}.
-     */
-    private final ArrayDeque<HbmEvent> mEvents = new ArrayDeque<>();
-
+    private HighBrightnessModeMetadata mHighBrightnessModeMetadata = null;
     HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
             String displayUniqueId, float brightnessMin, float brightnessMax,
             HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
-            Runnable hbmChangeCallback, Context context) {
+            Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, Context context) {
         this(new Injector(), handler, width, height, displayToken, displayUniqueId, brightnessMin,
-            brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, context);
+            brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, hbmMetadata, context);
     }
 
     @VisibleForTesting
     HighBrightnessModeController(Injector injector, Handler handler, int width, int height,
             IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax,
             HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
-            Runnable hbmChangeCallback, Context context) {
+            Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, Context context) {
         mInjector = injector;
         mContext = context;
         mClock = injector.getClock();
@@ -137,6 +130,7 @@
         mBrightnessMin = brightnessMin;
         mBrightnessMax = brightnessMax;
         mHbmChangeCallback = hbmChangeCallback;
+        mHighBrightnessModeMetadata = hbmMetadata;
         mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
         mSettingsObserver = new SettingsObserver(mHandler);
         mRecalcRunnable = this::recalculateTimeAllowance;
@@ -222,19 +216,22 @@
 
         // If we are starting or ending a high brightness mode session, store the current
         // session in mRunningStartTimeMillis, or the old one in mEvents.
-        final boolean wasHbmDrainingAvailableTime = mRunningStartTimeMillis != -1;
+        final long runningStartTime = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
+        final boolean wasHbmDrainingAvailableTime = runningStartTime != -1;
         final boolean shouldHbmDrainAvailableTime = mBrightness > mHbmData.transitionPoint
                 && !mIsHdrLayerPresent;
         if (wasHbmDrainingAvailableTime != shouldHbmDrainAvailableTime) {
             final long currentTime = mClock.uptimeMillis();
             if (shouldHbmDrainAvailableTime) {
-                mRunningStartTimeMillis = currentTime;
+                mHighBrightnessModeMetadata.setRunningStartTimeMillis(currentTime);
             } else {
-                mEvents.addFirst(new HbmEvent(mRunningStartTimeMillis, currentTime));
-                mRunningStartTimeMillis = -1;
+                final HbmEvent hbmEvent = new HbmEvent(runningStartTime, currentTime);
+                mHighBrightnessModeMetadata.addHbmEvent(hbmEvent);
+                mHighBrightnessModeMetadata.setRunningStartTimeMillis(-1);
 
                 if (DEBUG) {
-                    Slog.d(TAG, "New HBM event: " + mEvents.peekFirst());
+                    Slog.d(TAG, "New HBM event: "
+                            + mHighBrightnessModeMetadata.getHbmEventQueue().peekFirst());
                 }
             }
         }
@@ -260,6 +257,10 @@
         mSettingsObserver.stopObserving();
     }
 
+    void setHighBrightnessModeMetadata(HighBrightnessModeMetadata hbmInfo) {
+        mHighBrightnessModeMetadata = hbmInfo;
+    }
+
     void resetHbmData(int width, int height, IBinder displayToken, String displayUniqueId,
             HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg) {
         mWidth = width;
@@ -316,20 +317,22 @@
         pw.println("  mBrightnessMax=" + mBrightnessMax);
         pw.println("  remainingTime=" + calculateRemainingTime(mClock.uptimeMillis()));
         pw.println("  mIsTimeAvailable= " + mIsTimeAvailable);
-        pw.println("  mRunningStartTimeMillis=" + TimeUtils.formatUptime(mRunningStartTimeMillis));
+        pw.println("  mRunningStartTimeMillis="
+                + TimeUtils.formatUptime(mHighBrightnessModeMetadata.getRunningStartTimeMillis()));
         pw.println("  mIsThermalStatusWithinLimit=" + mIsThermalStatusWithinLimit);
         pw.println("  mIsBlockedByLowPowerMode=" + mIsBlockedByLowPowerMode);
         pw.println("  width*height=" + mWidth + "*" + mHeight);
         pw.println("  mEvents=");
         final long currentTime = mClock.uptimeMillis();
         long lastStartTime = currentTime;
-        if (mRunningStartTimeMillis != -1) {
-            lastStartTime = dumpHbmEvent(pw, new HbmEvent(mRunningStartTimeMillis, currentTime));
+        long runningStartTimeMillis = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
+        if (runningStartTimeMillis != -1) {
+            lastStartTime = dumpHbmEvent(pw, new HbmEvent(runningStartTimeMillis, currentTime));
         }
-        for (HbmEvent event : mEvents) {
-            if (lastStartTime > event.endTimeMillis) {
+        for (HbmEvent event : mHighBrightnessModeMetadata.getHbmEventQueue()) {
+            if (lastStartTime > event.getEndTimeMillis()) {
                 pw.println("    event: [normal brightness]: "
-                        + TimeUtils.formatDuration(lastStartTime - event.endTimeMillis));
+                        + TimeUtils.formatDuration(lastStartTime - event.getEndTimeMillis()));
             }
             lastStartTime = dumpHbmEvent(pw, event);
         }
@@ -338,12 +341,12 @@
     }
 
     private long dumpHbmEvent(PrintWriter pw, HbmEvent event) {
-        final long duration = event.endTimeMillis - event.startTimeMillis;
+        final long duration = event.getEndTimeMillis() - event.getStartTimeMillis();
         pw.println("    event: ["
-                + TimeUtils.formatUptime(event.startTimeMillis) + ", "
-                + TimeUtils.formatUptime(event.endTimeMillis) + "] ("
+                + TimeUtils.formatUptime(event.getStartTimeMillis()) + ", "
+                + TimeUtils.formatUptime(event.getEndTimeMillis()) + "] ("
                 + TimeUtils.formatDuration(duration) + ")");
-        return event.startTimeMillis;
+        return event.getStartTimeMillis();
     }
 
     private boolean isCurrentlyAllowed() {
@@ -372,13 +375,15 @@
 
         // First, lets see how much time we've taken for any currently running
         // session of HBM.
-        if (mRunningStartTimeMillis > 0) {
-            if (mRunningStartTimeMillis > currentTime) {
+        long runningStartTimeMillis = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
+        if (runningStartTimeMillis > 0) {
+            if (runningStartTimeMillis > currentTime) {
                 Slog.e(TAG, "Start time set to the future. curr: " + currentTime
-                        + ", start: " + mRunningStartTimeMillis);
-                mRunningStartTimeMillis = currentTime;
+                        + ", start: " + runningStartTimeMillis);
+                mHighBrightnessModeMetadata.setRunningStartTimeMillis(currentTime);
+                runningStartTimeMillis = currentTime;
             }
-            timeAlreadyUsed = currentTime - mRunningStartTimeMillis;
+            timeAlreadyUsed = currentTime - runningStartTimeMillis;
         }
 
         if (DEBUG) {
@@ -387,18 +392,19 @@
 
         // Next, lets iterate through the history of previous sessions and add those times.
         final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis;
-        Iterator<HbmEvent> it = mEvents.iterator();
+        Iterator<HbmEvent> it = mHighBrightnessModeMetadata.getHbmEventQueue().iterator();
         while (it.hasNext()) {
             final HbmEvent event = it.next();
 
             // If this event ended before the current Timing window, discard forever and ever.
-            if (event.endTimeMillis < windowstartTimeMillis) {
+            if (event.getEndTimeMillis() < windowstartTimeMillis) {
                 it.remove();
                 continue;
             }
 
-            final long startTimeMillis = Math.max(event.startTimeMillis, windowstartTimeMillis);
-            timeAlreadyUsed += event.endTimeMillis - startTimeMillis;
+            final long startTimeMillis = Math.max(event.getStartTimeMillis(),
+                            windowstartTimeMillis);
+            timeAlreadyUsed += event.getEndTimeMillis() - startTimeMillis;
         }
 
         if (DEBUG) {
@@ -425,17 +431,18 @@
         // Calculate the time at which we want to recalculate mIsTimeAvailable in case a lux or
         // brightness change doesn't happen before then.
         long nextTimeout = -1;
+        final ArrayDeque<HbmEvent> hbmEvents = mHighBrightnessModeMetadata.getHbmEventQueue();
         if (mBrightness > mHbmData.transitionPoint) {
             // if we're in high-lux now, timeout when we run out of allowed time.
             nextTimeout = currentTime + remainingTime;
-        } else if (!mIsTimeAvailable && mEvents.size() > 0) {
+        } else if (!mIsTimeAvailable && hbmEvents.size() > 0) {
             // If we are not allowed...timeout when the oldest event moved outside of the timing
             // window by at least minTime. Basically, we're calculating the soonest time we can
             // get {@code timeMinMillis} back to us.
             final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis;
-            final HbmEvent lastEvent = mEvents.peekLast();
+            final HbmEvent lastEvent = hbmEvents.peekLast();
             final long startTimePlusMinMillis =
-                    Math.max(windowstartTimeMillis, lastEvent.startTimeMillis)
+                    Math.max(windowstartTimeMillis, lastEvent.getStartTimeMillis())
                     + mHbmData.timeMinMillis;
             final long timeWhenMinIsGainedBack =
                     currentTime + (startTimePlusMinMillis - windowstartTimeMillis) - remainingTime;
@@ -459,9 +466,10 @@
                     + ", mUnthrottledBrightness: " + mUnthrottledBrightness
                     + ", mThrottlingReason: "
                         + BrightnessInfo.briMaxReasonToString(mThrottlingReason)
-                    + ", RunningStartTimeMillis: " + mRunningStartTimeMillis
+                    + ", RunningStartTimeMillis: "
+                        + mHighBrightnessModeMetadata.getRunningStartTimeMillis()
                     + ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1)
-                    + ", events: " + mEvents);
+                    + ", events: " + hbmEvents);
         }
 
         if (nextTimeout != -1) {
@@ -588,25 +596,6 @@
         }
     }
 
-    /**
-     * Represents an event in which High Brightness Mode was enabled.
-     */
-    private static class HbmEvent {
-        public long startTimeMillis;
-        public long endTimeMillis;
-
-        HbmEvent(long startTimeMillis, long endTimeMillis) {
-            this.startTimeMillis = startTimeMillis;
-            this.endTimeMillis = endTimeMillis;
-        }
-
-        @Override
-        public String toString() {
-            return "[Event: {" + startTimeMillis + ", " + endTimeMillis + "}, total: "
-                    + ((endTimeMillis - startTimeMillis) / 1000) + "]";
-        }
-    }
-
     @VisibleForTesting
     class HdrListener extends SurfaceControlHdrLayerInfoListener {
         @Override
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java b/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java
new file mode 100644
index 0000000..37234ff
--- /dev/null
+++ b/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import java.util.ArrayDeque;
+
+
+/**
+ * Represents High Brightness Mode metadata associated
+ * with a specific internal physical display.
+ * Required for separately storing data like time information,
+ * and related events when display was in HBM mode per
+ * physical internal display.
+ */
+class HighBrightnessModeMetadata {
+    /**
+     * Queue of previous HBM-events ordered from most recent to least recent.
+     * Meant to store only the events that fall into the most recent
+     * {@link HighBrightnessModeData#timeWindowMillis mHbmData.timeWindowMillis}.
+     */
+    private final ArrayDeque<HbmEvent> mEvents = new ArrayDeque<>();
+
+    /**
+     * If HBM is currently running, this is the start time for the current HBM session.
+     */
+    private long mRunningStartTimeMillis = -1;
+
+    public long getRunningStartTimeMillis() {
+        return mRunningStartTimeMillis;
+    }
+
+    public void setRunningStartTimeMillis(long setTime) {
+        mRunningStartTimeMillis = setTime;
+    }
+
+    public ArrayDeque<HbmEvent> getHbmEventQueue() {
+        return mEvents;
+    }
+
+    public void addHbmEvent(HbmEvent hbmEvent) {
+        mEvents.addFirst(hbmEvent);
+    }
+}
+
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessUtils.java b/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
index fd4e296..d5aeba1 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
@@ -17,6 +17,7 @@
 package com.android.server.display.brightness;
 
 import android.os.PowerManager;
+import android.util.MathUtils;
 
 import com.android.server.display.DisplayBrightnessState;
 
@@ -33,6 +34,14 @@
     }
 
     /**
+     * Clamps the brightness value in the maximum and the minimum brightness range
+     */
+    public static float clampAbsoluteBrightness(float value) {
+        return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN,
+                PowerManager.BRIGHTNESS_MAX);
+    }
+
+    /**
      * A utility to construct the DisplayBrightnessState
      */
     public static DisplayBrightnessState constructDisplayBrightnessState(
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index bdc8d9d..e003ecb 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -18,9 +18,12 @@
 
 import android.content.Context;
 import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
 import android.util.IndentingPrintWriter;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.BrightnessSetting;
 import com.android.server.display.DisplayBrightnessState;
 import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
 
@@ -31,19 +34,67 @@
  * display. Applies the chosen brightness.
  */
 public final class DisplayBrightnessController {
+    // The ID of the display tied to this DisplayBrightnessController
     private final int mDisplayId;
+
+    // The lock which is to be used to synchronize the resources being used in this class
+    private final Object mLock = new Object();
+
+    // The default screen brightness to be used when no value is available in BrightnessSetting.
+    private final float mScreenBrightnessDefault;
+
+    // This is used to persist the changes happening to the brightness.
+    private final BrightnessSetting mBrightnessSetting;
+
+    // A runnable to update the clients registered via DisplayManagerGlobal
+    // .EVENT_DISPLAY_BRIGHTNESS_CHANGED about the brightness change. Called when
+    // mCurrentScreenBrightness is updated.
+    private Runnable mOnBrightnessChangeRunnable;
+
+    // The screen brightness that has changed but not taken effect yet. If this is different
+    // from the current screen brightness then this is coming from something other than us
+    // and should be considered a user interaction.
+    @GuardedBy("mLock")
+    private float mPendingScreenBrightness;
+
+    // The last observed screen brightness, either set by us or by the settings app on
+    // behalf of the user.
+    @GuardedBy("mLock")
+    private float mCurrentScreenBrightness;
+
+    // The last brightness that was set by the user and not temporary. Set to
+    // PowerManager.BRIGHTNESS_INVALID_FLOAT when a brightness has yet to be recorded.
+    @GuardedBy("mLock")
+    private float mLastUserSetScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+
+    // The listener which is to be notified everytime there is a change in the brightness in the
+    // BrightnessSetting.
+    private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener;
+
     // Selects an appropriate strategy based on the request provided by the clients.
+    @GuardedBy("mLock")
     private DisplayBrightnessStrategySelector mDisplayBrightnessStrategySelector;
+
+    // Currently selected DisplayBrightnessStrategy.
+    @GuardedBy("mLock")
     private DisplayBrightnessStrategy mDisplayBrightnessStrategy;
 
     /**
      * The constructor of DisplayBrightnessController.
      */
-    public DisplayBrightnessController(Context context, Injector injector, int displayId) {
+    public DisplayBrightnessController(Context context, Injector injector, int displayId,
+            float defaultScreenBrightness, BrightnessSetting brightnessSetting,
+            Runnable onBrightnessChangeRunnable) {
         if (injector == null) {
             injector = new Injector();
         }
         mDisplayId = displayId;
+        // TODO: b/186428377 update brightness setting when display changes
+        mBrightnessSetting = brightnessSetting;
+        mPendingScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        mCurrentScreenBrightness = getScreenBrightnessSetting();
+        mOnBrightnessChangeRunnable = onBrightnessChangeRunnable;
+        mScreenBrightnessDefault = BrightnessUtils.clampAbsoluteBrightness(defaultScreenBrightness);
         mDisplayBrightnessStrategySelector = injector.getDisplayBrightnessStrategySelector(context,
                 displayId);
     }
@@ -61,25 +112,20 @@
     public DisplayBrightnessState updateBrightness(
             DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
             int targetDisplayState) {
-        mDisplayBrightnessStrategy =
-                mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
-                        targetDisplayState);
-        return mDisplayBrightnessStrategy.updateBrightness(displayPowerRequest);
+        synchronized (mLock) {
+            mDisplayBrightnessStrategy = mDisplayBrightnessStrategySelector.selectStrategy(
+                    displayPowerRequest, targetDisplayState);
+            return mDisplayBrightnessStrategy.updateBrightness(displayPowerRequest);
+        }
     }
 
     /**
      * Sets the temporary brightness
      */
     public void setTemporaryBrightness(Float temporaryBrightness) {
-        mDisplayBrightnessStrategySelector.getTemporaryDisplayBrightnessStrategy()
-                .setTemporaryScreenBrightness(temporaryBrightness);
-    }
-
-    /**
-     * Returns the current selected DisplayBrightnessStrategy
-     */
-    public DisplayBrightnessStrategy getCurrentDisplayBrightnessStrategy() {
-        return mDisplayBrightnessStrategy;
+        synchronized (mLock) {
+            setTemporaryBrightnessLocked(temporaryBrightness);
+        }
     }
 
     /**
@@ -87,7 +133,140 @@
      * brightness when dozing
      */
     public boolean isAllowAutoBrightnessWhileDozingConfig() {
-        return mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozingConfig();
+        synchronized (mLock) {
+            return mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozingConfig();
+        }
+    }
+
+    /**
+     * Sets the current screen brightness to the supplied value, and notifies all the listeners
+     * requesting for change events on brightness change.
+     */
+    public void setCurrentScreenBrightness(float brightnessValue) {
+        synchronized (mLock) {
+            if (brightnessValue != mCurrentScreenBrightness) {
+                mCurrentScreenBrightness = brightnessValue;
+                mOnBrightnessChangeRunnable.run();
+            }
+        }
+    }
+
+    /**
+     * Returns the last observed screen brightness.
+     */
+    public float getCurrentBrightness() {
+        synchronized (mLock) {
+            return mCurrentScreenBrightness;
+        }
+    }
+
+    /**
+     * Returns the screen brightness which has changed but has not taken any effect so far.
+     */
+    public float getPendingScreenBrightness() {
+        synchronized (mLock) {
+            return mPendingScreenBrightness;
+        }
+    }
+
+    /**
+     * Sets the pending screen brightness setting, representing a value which is requested, but not
+     * yet processed.
+     * @param brightnessValue The value to which the pending screen brightness is to be set.
+     */
+    public void setPendingScreenBrightness(float brightnessValue) {
+        synchronized (mLock) {
+            mPendingScreenBrightness = brightnessValue;
+        }
+    }
+
+    /**
+     * We want to return true if the user has set the screen brightness.
+     * RBC on, off, and intensity changes will return false.
+     * Slider interactions whilst in RBC will return true, just as when in non-rbc.
+     */
+    public boolean updateUserSetScreenBrightness() {
+        synchronized (mLock) {
+            if (!BrightnessUtils.isValidBrightnessValue(mPendingScreenBrightness)) {
+                return false;
+            }
+            if (mCurrentScreenBrightness == mPendingScreenBrightness) {
+                mPendingScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+                setTemporaryBrightnessLocked(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+                return false;
+            }
+            setCurrentScreenBrightness(mPendingScreenBrightness);
+            mLastUserSetScreenBrightness = mPendingScreenBrightness;
+            mPendingScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+            setTemporaryBrightnessLocked(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+            return true;
+        }
+    }
+
+    /**
+     * Registers the BrightnessSettingListener with the BrightnessSetting, which will be notified
+     * everytime there is a change in the brightness.
+     */
+    public void registerBrightnessSettingChangeListener(
+            BrightnessSetting.BrightnessSettingListener brightnessSettingListener) {
+        mBrightnessSettingListener = brightnessSettingListener;
+        mBrightnessSetting.registerListener(mBrightnessSettingListener);
+    }
+
+    /**
+     * Returns the last user set brightness which is not temporary.
+     */
+    public float getLastUserSetScreenBrightness() {
+        synchronized (mLock) {
+            return mLastUserSetScreenBrightness;
+        }
+    }
+
+    /**
+     * Returns the current screen brightnessSetting which is responsible for saving the brightness
+     * in the persistent store
+     */
+    public float getScreenBrightnessSetting() {
+        float brightness = mBrightnessSetting.getBrightness();
+        synchronized (mLock) {
+            if (Float.isNaN(brightness)) {
+                brightness = mScreenBrightnessDefault;
+            }
+            return BrightnessUtils.clampAbsoluteBrightness(brightness);
+        }
+    }
+
+    /**
+     * Notifies the brightnessSetting to persist the supplied brightness value.
+     */
+    public void setBrightness(float brightnessValue) {
+        // Update the setting, which will eventually call back into DPC to have us actually
+        // update the display with the new value.
+        mBrightnessSetting.setBrightness(brightnessValue);
+    }
+
+    /**
+     * Sets the current screen brightness, and notifies the BrightnessSetting about the change.
+     */
+    public void updateScreenBrightnessSetting(float brightnessValue) {
+        synchronized (mLock) {
+            if (!BrightnessUtils.isValidBrightnessValue(brightnessValue)
+                    || brightnessValue == mCurrentScreenBrightness) {
+                return;
+            }
+            setCurrentScreenBrightness(brightnessValue);
+            setBrightness(brightnessValue);
+        }
+    }
+
+    /**
+     * Stops the associated listeners when the display is stopped. Invoked when the {@link
+     * #mDisplayId} is being removed.
+     */
+    public void stop() {
+        if (mBrightnessSetting != null) {
+            mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
+        }
     }
 
     /**
@@ -99,12 +278,19 @@
         writer.println();
         writer.println("DisplayBrightnessController:");
         writer.println("  mDisplayId=: " + mDisplayId);
-        if (mDisplayBrightnessStrategy != null) {
-            writer.println("  Last selected DisplayBrightnessStrategy= "
-                    + mDisplayBrightnessStrategy.getName());
+        writer.println("  mScreenBrightnessDefault=" + mScreenBrightnessDefault);
+        synchronized (mLock) {
+            writer.println("  mPendingScreenBrightness=" + mPendingScreenBrightness);
+            writer.println("  mCurrentScreenBrightness=" + mCurrentScreenBrightness);
+            writer.println("  mLastUserSetScreenBrightness="
+                    + mLastUserSetScreenBrightness);
+            if (mDisplayBrightnessStrategy != null) {
+                writer.println("  Last selected DisplayBrightnessStrategy= "
+                        + mDisplayBrightnessStrategy.getName());
+            }
+            IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
+            mDisplayBrightnessStrategySelector.dump(ipw);
         }
-        IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
-        mDisplayBrightnessStrategySelector.dump(ipw);
     }
 
     @VisibleForTesting
@@ -114,4 +300,26 @@
             return new DisplayBrightnessStrategySelector(context, /* injector= */ null, displayId);
         }
     }
+
+    @VisibleForTesting
+    BrightnessSetting.BrightnessSettingListener getBrightnessSettingListenerLocked() {
+        return mBrightnessSettingListener;
+    }
+
+    /**
+     * Returns the current selected DisplayBrightnessStrategy
+     */
+    @VisibleForTesting
+    DisplayBrightnessStrategy getCurrentDisplayBrightnessStrategyLocked() {
+        synchronized (mLock) {
+            return mDisplayBrightnessStrategy;
+        }
+    }
+
+    private void setTemporaryBrightnessLocked(float temporaryBrightness) {
+        synchronized (mLock) {
+            mDisplayBrightnessStrategySelector.getTemporaryDisplayBrightnessStrategy()
+                    .setTemporaryScreenBrightness(temporaryBrightness);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index c1780a3..e5357f6 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -16,6 +16,9 @@
 
 package com.android.server.dreams;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
+
+import android.app.ActivityTaskManager;
 import android.app.BroadcastOptions;
 import android.content.ComponentName;
 import android.content.Context;
@@ -62,6 +65,7 @@
     private final Context mContext;
     private final Handler mHandler;
     private final Listener mListener;
+    private final ActivityTaskManager mActivityTaskManager;
 
     private final Intent mDreamingStartedIntent = new Intent(Intent.ACTION_DREAMING_STARTED)
             .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -89,6 +93,7 @@
         mContext = context;
         mHandler = handler;
         mListener = listener;
+        mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
         mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         mCloseNotificationShadeIntent.putExtra("reason", "dream");
     }
@@ -272,6 +277,9 @@
                     mSentStartBroadcast = false;
                 }
 
+                mActivityTaskManager.removeRootTasksWithActivityTypes(
+                        new int[] {ACTIVITY_TYPE_DREAM});
+
                 mListener.onDreamStopped(dream.mToken);
             }
         } finally {
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index c47d749..a4d2d03 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -462,7 +462,7 @@
     }
 
     protected void requestStopDreamFromShell() {
-        stopDreamInternal(true, "stopping dream from shell");
+        stopDreamInternal(false, "stopping dream from shell");
     }
 
     private void stopDreamInternal(boolean immediate, String reason) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index ad8e35d..b919330 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -1152,8 +1152,8 @@
                 throw new IOException("Root has not present");
             }
             return ApkChecksums.verityHashForFile(new File(filename), hashInfo.rawRootHash);
-        } catch (IOException ignore) {
-            Slog.e(TAG, "ERROR: could not load root hash from incremental install");
+        } catch (IOException e) {
+            Slog.i(TAG, "Could not obtain verity root hash", e);
         }
         return null;
     }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 3340e12..762d1f6 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -262,7 +262,7 @@
 
     // We need to keep process uid within Integer.MAX_VALUE.
     @VisibleForTesting
-    static final int MAX_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE;
+    static final int MAX_USER_ID = UserHandle.MAX_SECONDARY_USER_ID;
 
     // Max size of the queue of recently removed users
     @VisibleForTesting
@@ -1779,28 +1779,6 @@
     }
 
     @Override
-    public boolean isMediaSharedWithParent(@UserIdInt int userId) {
-        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
-                "isMediaSharedWithParent");
-        synchronized (mUsersLock) {
-            UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
-            return userTypeDetails != null ? userTypeDetails.isProfile()
-                    && userTypeDetails.isMediaSharedWithParent() : false;
-        }
-    }
-
-    @Override
-    public boolean isCredentialSharableWithParent(@UserIdInt int userId) {
-        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
-                "isCredentialSharableWithParent");
-        synchronized (mUsersLock) {
-            UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
-            return userTypeDetails != null && userTypeDetails.isProfile()
-                    && userTypeDetails.isCredentialSharableWithParent();
-        }
-    }
-
-    @Override
     public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
         checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
                 "isUserUnlockingOrUnlocked");
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index ddf3692..f86ee90 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -151,20 +151,6 @@
     private final @Nullable int[] mDarkThemeBadgeColors;
 
     /**
-     * Denotes if the user shares media with its parent user.
-     *
-     * <p> Default value is false
-     */
-    private final boolean mIsMediaSharedWithParent;
-
-    /**
-     * Denotes if the user shares encryption credentials with its parent user.
-     *
-     * <p> Default value is false
-     */
-    private final boolean mIsCredentialSharableWithParent;
-
-    /**
      * The default {@link UserProperties} for the user type.
      * <p> The uninitialized value of each property is implied by {@link UserProperties.Builder}.
      */
@@ -180,8 +166,6 @@
             @Nullable Bundle defaultSystemSettings,
             @Nullable Bundle defaultSecureSettings,
             @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters,
-            boolean isMediaSharedWithParent,
-            boolean isCredentialSharableWithParent,
             @NonNull UserProperties defaultUserProperties) {
         this.mName = name;
         this.mEnabled = enabled;
@@ -201,8 +185,6 @@
         this.mBadgeLabels = badgeLabels;
         this.mBadgeColors = badgeColors;
         this.mDarkThemeBadgeColors = darkThemeBadgeColors;
-        this.mIsMediaSharedWithParent = isMediaSharedWithParent;
-        this.mIsCredentialSharableWithParent = isCredentialSharableWithParent;
         this.mDefaultUserProperties = defaultUserProperties;
     }
 
@@ -309,21 +291,6 @@
         return mDarkThemeBadgeColors[Math.min(badgeIndex, mDarkThemeBadgeColors.length - 1)];
     }
 
-    /**
-     * Returns true if the user has shared media with parent user or false otherwise.
-     */
-    public boolean isMediaSharedWithParent() {
-        return mIsMediaSharedWithParent;
-    }
-
-    /**
-     * Returns true if the user has shared encryption credential with parent user or
-     * false otherwise.
-     */
-    public boolean isCredentialSharableWithParent() {
-        return mIsCredentialSharableWithParent;
-    }
-
 
     /**
      * Returns the reference to the default {@link UserProperties} for this type of user.
@@ -437,8 +404,6 @@
         private @DrawableRes int mIconBadge = Resources.ID_NULL;
         private @DrawableRes int mBadgePlain = Resources.ID_NULL;
         private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
-        private boolean mIsMediaSharedWithParent = false;
-        private boolean mIsCredentialSharableWithParent = false;
         // Default UserProperties cannot be null but for efficiency we don't initialize it now.
         // If it isn't set explicitly, {@link UserProperties.Builder#build()} will be used.
         private @Nullable UserProperties mDefaultUserProperties = null;
@@ -533,24 +498,6 @@
         }
 
         /**
-         * Sets shared media property for the user.
-         * @param isMediaSharedWithParent the value to be set, true or false
-         */
-        public Builder setIsMediaSharedWithParent(boolean isMediaSharedWithParent) {
-            mIsMediaSharedWithParent = isMediaSharedWithParent;
-            return this;
-        }
-
-        /**
-         * Sets shared media property for the user.
-         * @param isCredentialSharableWithParent  the value to be set, true or false
-         */
-        public Builder setIsCredentialSharableWithParent(boolean isCredentialSharableWithParent) {
-            mIsCredentialSharableWithParent = isCredentialSharableWithParent;
-            return this;
-        }
-
-        /**
          * Sets (replacing if necessary) the default UserProperties object for this user type.
          * Takes a builder, rather than a built object, to efficiently ensure that a fresh copy of
          * properties is stored (since it later might be modified by UserProperties#updateFromXml).
@@ -609,8 +556,6 @@
                     mDefaultSystemSettings,
                     mDefaultSecureSettings,
                     mDefaultCrossProfileIntentFilters,
-                    mIsMediaSharedWithParent,
-                    mIsCredentialSharableWithParent,
                     getDefaultUserProperties());
         }
 
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index edb2a4be3b..b8c57b8 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -122,8 +122,6 @@
                 .setMaxAllowedPerParent(1)
                 .setLabel(0)
                 .setDefaultRestrictions(null)
-                .setIsMediaSharedWithParent(true)
-                .setIsCredentialSharableWithParent(true)
                 .setDefaultCrossProfileIntentFilters(getDefaultCloneCrossProfileIntentFilter())
                 .setDefaultUserProperties(new UserProperties.Builder()
                         .setStartWithParent(true)
@@ -135,7 +133,10 @@
                         .setCrossProfileIntentFilterAccessControl(
                                 UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
                         .setCrossProfileIntentResolutionStrategy(UserProperties
-                                .CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_NO_FILTERING));
+                                .CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_NO_FILTERING)
+                        .setMediaSharedWithParent(true)
+                        .setCredentialShareableWithParent(true)
+                );
     }
 
     /**
@@ -167,11 +168,11 @@
                 .setDefaultRestrictions(getDefaultManagedProfileRestrictions())
                 .setDefaultSecureSettings(getDefaultManagedProfileSecureSettings())
                 .setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter())
-                .setIsCredentialSharableWithParent(true)
                 .setDefaultUserProperties(new UserProperties.Builder()
                         .setStartWithParent(true)
                         .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
-                        .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE));
+                        .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
+                        .setCredentialShareableWithParent(true));
     }
 
     /**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 60751e6..5a0c344 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -664,7 +664,7 @@
                     dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj);
                     break;
                 case MSG_DISPATCH_SHOW_RECENTS:
-                    showRecents();
+                    showRecentApps(false);
                     break;
                 case MSG_DISPATCH_SHOW_GLOBAL_ACTIONS:
                     showGlobalActionsInternal();
@@ -2927,7 +2927,7 @@
                 break;
             case KeyEvent.KEYCODE_RECENT_APPS:
                 if (down && repeatCount == 0) {
-                    showRecents();
+                    showRecentApps(false /* triggeredFromAltTab */);
                 }
                 return key_consumed;
             case KeyEvent.KEYCODE_APP_SWITCH:
@@ -3110,23 +3110,22 @@
                 }
                 break;
             case KeyEvent.KEYCODE_TAB:
-                if (down) {
-                    if (event.isMetaPressed()) {
-                        if (!keyguardOn && isUserSetupComplete()) {
-                            showRecents();
+                if (down && event.isMetaPressed()) {
+                    if (!keyguardOn && isUserSetupComplete()) {
+                        showRecentApps(false);
+                        return key_consumed;
+                    }
+                } else if (down && repeatCount == 0) {
+                    // Display task switcher for ALT-TAB.
+                    if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) {
+                        final int shiftlessModifiers =
+                                event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
+                        if (KeyEvent.metaStateHasModifiers(
+                                shiftlessModifiers, KeyEvent.META_ALT_ON)) {
+                            mRecentAppsHeldModifiers = shiftlessModifiers;
+                            showRecentApps(true);
                             return key_consumed;
                         }
-                    } else {
-                        // Display task switcher for ALT-TAB.
-                        if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) {
-                            final int modifiers = event.getModifiers();
-                            if (KeyEvent.metaStateHasModifiers(modifiers, KeyEvent.META_ALT_ON)) {
-                                mRecentAppsHeldModifiers = modifiers;
-                                showRecentsFromAltTab(KeyEvent.metaStateHasModifiers(modifiers,
-                                        KeyEvent.META_SHIFT_ON));
-                                return key_consumed;
-                            }
-                        }
                     }
                 }
                 break;
@@ -3662,19 +3661,11 @@
         mHandler.obtainMessage(MSG_DISPATCH_SHOW_RECENTS).sendToTarget();
     }
 
-    private void showRecents() {
+    private void showRecentApps(boolean triggeredFromAltTab) {
         mPreloadedRecentApps = false; // preloading no longer needs to be canceled
         StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
         if (statusbar != null) {
-            statusbar.showRecentApps(false /* triggeredFromAltTab */, false /* forward */);
-        }
-    }
-
-    private void showRecentsFromAltTab(boolean forward) {
-        mPreloadedRecentApps = false; // preloading no longer needs to be canceled
-        StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
-        if (statusbar != null) {
-            statusbar.showRecentApps(true /* triggeredFromAltTab */, forward);
+            statusbar.showRecentApps(triggeredFromAltTab);
         }
     }
 
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index fd64c75..7beb1ed 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -36,12 +36,14 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.Preconditions;
 import com.android.server.PackageWatchdog;
 import com.android.server.PackageWatchdog.FailureReasons;
 import com.android.server.PackageWatchdog.PackageHealthObserver;
 import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
+import com.android.server.SystemConfig;
 import com.android.server.pm.ApexManager;
 
 import java.io.BufferedReader;
@@ -358,6 +360,13 @@
     private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage,
             @FailureReasons int rollbackReason) {
         assertInWorkerThread();
+
+        if (isAutomaticRollbackDenied(SystemConfig.getInstance(), failedPackage)) {
+            Slog.d(TAG, "Automatic rollback not allowed for package "
+                    + failedPackage.getPackageName());
+            return;
+        }
+
         final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
         int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason);
         final String failedPackageToLog;
@@ -420,6 +429,17 @@
     }
 
     /**
+     * Returns true if this package is not eligible for automatic rollback.
+     */
+    @VisibleForTesting
+    @AnyThread
+    public static boolean isAutomaticRollbackDenied(SystemConfig systemConfig,
+            VersionedPackage versionedPackage) {
+        return systemConfig.getAutomaticRollbackDenylistedPackages()
+            .contains(versionedPackage.getPackageName());
+    }
+
+    /**
      * Two-phase rollback:
      * 1. roll back rebootless apexes first
      * 2. roll back all remaining rollbacks if native crash doesn't stop after (1) is done
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index f973f5c..8068c6f 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -249,7 +249,7 @@
         targetDir.mkdirs();
         File targetFile = new File(targetDir, sourceFile.getName());
 
-        boolean fallbackToCopy = !isLinkPossible(sourceFile, targetFile);
+        boolean fallbackToCopy = !isLinkPossible(sourceFile, targetDir);
         if (!fallbackToCopy) {
             try {
                 // Create a hard link to avoid copy
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
index 411b2fa..e148a48 100644
--- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -72,8 +72,7 @@
                 new Intent(RecognitionService.SERVICE_INTERFACE).setComponent(serviceName),
                 Context.BIND_AUTO_CREATE
                         | Context.BIND_FOREGROUND_SERVICE
-                        | Context.BIND_INCLUDE_CAPABILITIES
-                        | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
+                        | Context.BIND_INCLUDE_CAPABILITIES,
                 userId,
                 IRecognitionService.Stub::asInterface);
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 05b3ce7..5521384 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -39,7 +39,7 @@
 
     void cancelPreloadRecentApps();
 
-    void showRecentApps(boolean triggeredFromAltTab, boolean forward);
+    void showRecentApps(boolean triggeredFromAltTab);
 
     void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 0f49981..83f4805 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -454,10 +454,10 @@
         }
 
         @Override
-        public void showRecentApps(boolean triggeredFromAltTab, boolean forward) {
+        public void showRecentApps(boolean triggeredFromAltTab) {
             if (mBar != null) {
                 try {
-                    mBar.showRecentApps(triggeredFromAltTab, forward);
+                    mBar.showRecentApps(triggeredFromAltTab);
                 } catch (RemoteException ex) {}
             }
         }
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index ca4a32f..099c9ae 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -16,9 +16,6 @@
 
 package com.android.server.vcn;
 
-import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
-import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
-import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
 import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
@@ -48,7 +45,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.vcn.util.PersistableBundleUtils;
 import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
 import java.util.ArrayList;
@@ -109,6 +105,12 @@
 
     @NonNull private TelephonySubscriptionSnapshot mCurrentSnapshot;
 
+    @NonNull
+    private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+            (int logicalSlotIndex, int subscriptionId, int carrierId, int specificCarrierId) ->
+                    handleActionCarrierConfigChanged(logicalSlotIndex, subscriptionId);
+
+
     public TelephonySubscriptionTracker(
             @NonNull Context context,
             @NonNull Handler handler,
@@ -149,13 +151,14 @@
     public void register() {
         final HandlerExecutor executor = new HandlerExecutor(mHandler);
         final IntentFilter filter = new IntentFilter();
-        filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
         filter.addAction(ACTION_MULTI_SIM_CONFIG_CHANGED);
 
         mContext.registerReceiver(this, filter, null, mHandler);
         mSubscriptionManager.addOnSubscriptionsChangedListener(
                 executor, mSubscriptionChangedListener);
         mTelephonyManager.registerTelephonyCallback(executor, mActiveDataSubIdListener);
+        mCarrierConfigManager.registerCarrierConfigChangeListener(executor,
+                mCarrierConfigChangeListener);
 
         registerCarrierPrivilegesCallbacks();
     }
@@ -197,6 +200,7 @@
         mContext.unregisterReceiver(this);
         mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionChangedListener);
         mTelephonyManager.unregisterTelephonyCallback(mActiveDataSubIdListener);
+        mCarrierConfigManager.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
 
         unregisterCarrierPrivilegesCallbacks();
     }
@@ -273,7 +277,7 @@
     }
 
     /**
-     * Broadcast receiver for ACTION_CARRIER_CONFIG_CHANGED
+     * Broadcast receiver for ACTION_MULTI_SIM_CONFIG_CHANGED
      *
      * <p>The broadcast receiver is registered with mHandler, so callbacks & broadcasts are all
      * serialized on mHandler, avoiding the need for locking.
@@ -281,9 +285,6 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         switch (intent.getAction()) {
-            case ACTION_CARRIER_CONFIG_CHANGED:
-                handleActionCarrierConfigChanged(context, intent);
-                break;
             case ACTION_MULTI_SIM_CONFIG_CHANGED:
                 handleActionMultiSimConfigChanged(context, intent);
                 break;
@@ -310,26 +311,21 @@
         handleSubscriptionsChanged();
     }
 
-    private void handleActionCarrierConfigChanged(Context context, Intent intent) {
-        // Accept sticky broadcasts; if CARRIER_CONFIG_CHANGED was previously broadcast and it
-        // already was for an identified carrier, we can stop waiting for initial load to complete
-        final int subId = intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID);
-        final int slotId = intent.getIntExtra(EXTRA_SLOT_INDEX, INVALID_SIM_SLOT_INDEX);
-
+    private void handleActionCarrierConfigChanged(int slotId, int subId) {
         if (slotId == INVALID_SIM_SLOT_INDEX) {
             return;
         }
 
         if (SubscriptionManager.isValidSubscriptionId(subId)) {
-            final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
+            // Get only configs as needed to save memory.
+            final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId,
+                    VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
             if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) {
                 mReadySubIdsBySlotId.put(slotId, subId);
 
-                final PersistableBundle minimized =
-                        PersistableBundleUtils.minimizeBundle(
-                                carrierConfig, VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
-                if (minimized != null) {
-                    mSubIdToCarrierConfigMap.put(subId, new PersistableBundleWrapper(minimized));
+                if (!carrierConfig.isEmpty()) {
+                    mSubIdToCarrierConfigMap.put(subId,
+                            new PersistableBundleWrapper(carrierConfig));
                 }
                 handleSubscriptionsChanged();
             }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
new file mode 100644
index 0000000..49b125c
--- /dev/null
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wallpaper;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.server.wallpaper.WallpaperUtils.RECORD_FILE;
+import static com.android.server.wallpaper.WallpaperUtils.RECORD_LOCK_FILE;
+import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER;
+import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.ImageDecoder;
+import android.graphics.Rect;
+import android.os.FileUtils;
+import android.os.SELinux;
+import android.util.Slog;
+import android.view.DisplayInfo;
+
+import com.android.server.utils.TimingsTraceAndSlog;
+
+import libcore.io.IoUtils;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+
+/**
+ * Helper file for wallpaper cropping
+ * Meant to have a single instance, only used by the WallpaperManagerService
+ */
+class WallpaperCropper {
+
+    private static final String TAG = WallpaperCropper.class.getSimpleName();
+    private static final boolean DEBUG = false;
+    private static final boolean DEBUG_CROP = true;
+
+    private final WallpaperDisplayHelper mWallpaperDisplayHelper;
+
+    WallpaperCropper(WallpaperDisplayHelper wallpaperDisplayHelper) {
+        mWallpaperDisplayHelper = wallpaperDisplayHelper;
+    }
+
+    /**
+     * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
+     * for display.
+     *
+     * This will generate the crop and write it in the file
+     */
+    void generateCrop(WallpaperData wallpaper) {
+        TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+        t.traceBegin("WPMS.generateCrop");
+        generateCropInternal(wallpaper);
+        t.traceEnd();
+    }
+
+    private void generateCropInternal(WallpaperData wallpaper) {
+        boolean success = false;
+
+        // Only generate crop for default display.
+        final WallpaperDisplayHelper.DisplayData wpData =
+                mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
+        final Rect cropHint = new Rect(wallpaper.cropHint);
+        final DisplayInfo displayInfo = mWallpaperDisplayHelper.getDisplayInfo(DEFAULT_DISPLAY);
+
+        if (DEBUG) {
+            Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
+                    + Integer.toHexString(wallpaper.mWhich)
+                    + " to " + wallpaper.cropFile.getName()
+                    + " crop=(" + cropHint.width() + 'x' + cropHint.height()
+                    + ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')');
+        }
+
+        // Analyse the source; needed in multiple cases
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inJustDecodeBounds = true;
+        BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
+        if (options.outWidth <= 0 || options.outHeight <= 0) {
+            Slog.w(TAG, "Invalid wallpaper data");
+            success = false;
+        } else {
+            boolean needCrop = false;
+            boolean needScale;
+
+            // Empty crop means use the full image
+            if (cropHint.isEmpty()) {
+                cropHint.left = cropHint.top = 0;
+                cropHint.right = options.outWidth;
+                cropHint.bottom = options.outHeight;
+            } else {
+                // force the crop rect to lie within the measured bounds
+                int dx = cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0;
+                int dy = cropHint.bottom > options.outHeight
+                        ? options.outHeight - cropHint.bottom : 0;
+                cropHint.offset(dx, dy);
+
+                // If the crop hint was larger than the image we just overshot. Patch things up.
+                if (cropHint.left < 0) {
+                    cropHint.left = 0;
+                }
+                if (cropHint.top < 0) {
+                    cropHint.top = 0;
+                }
+
+                // Don't bother cropping if what we're left with is identity
+                needCrop = (options.outHeight > cropHint.height()
+                        || options.outWidth > cropHint.width());
+            }
+
+            // scale if the crop height winds up not matching the recommended metrics
+            needScale = cropHint.height() > wpData.mHeight
+                    || cropHint.height() > GLHelper.getMaxTextureSize()
+                    || cropHint.width() > GLHelper.getMaxTextureSize();
+
+            //make sure screen aspect ratio is preserved if width is scaled under screen size
+            if (needScale) {
+                final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height();
+                final int newWidth = (int) (cropHint.width() * scaleByHeight);
+                if (newWidth < displayInfo.logicalWidth) {
+                    final float screenAspectRatio =
+                            (float) displayInfo.logicalHeight / (float) displayInfo.logicalWidth;
+                    cropHint.bottom = (int) (cropHint.width() * screenAspectRatio);
+                    needCrop = true;
+                }
+            }
+
+            if (DEBUG_CROP) {
+                Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
+                Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
+                Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
+                Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
+            }
+
+            if (!needCrop && !needScale) {
+                // Simple case:  the nominal crop fits what we want, so we take
+                // the whole thing and just copy the image file directly.
+
+                // TODO: It is not accurate to estimate bitmap size without decoding it,
+                //  may be we can try to remove this optimized way in the future,
+                //  that means, we will always go into the 'else' block.
+
+                success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
+
+                if (!success) {
+                    wallpaper.cropFile.delete();
+                    // TODO: fall back to default wallpaper in this case
+                }
+
+                if (DEBUG) {
+                    long estimateSize = (long) options.outWidth * options.outHeight * 4;
+                    Slog.v(TAG, "Null crop of new wallpaper, estimate size="
+                            + estimateSize + ", success=" + success);
+                }
+            } else {
+                // Fancy case: crop and scale.  First, we decode and scale down if appropriate.
+                FileOutputStream f = null;
+                BufferedOutputStream bos = null;
+                try {
+                    // This actually downsamples only by powers of two, but that's okay; we do
+                    // a proper scaling blit later.  This is to minimize transient RAM use.
+                    // We calculate the largest power-of-two under the actual ratio rather than
+                    // just let the decode take care of it because we also want to remap where the
+                    // cropHint rectangle lies in the decoded [super]rect.
+                    final int actualScale = cropHint.height() / wpData.mHeight;
+                    int scale = 1;
+                    while (2 * scale <= actualScale) {
+                        scale *= 2;
+                    }
+                    options.inSampleSize = scale;
+                    options.inJustDecodeBounds = false;
+
+                    final Rect estimateCrop = new Rect(cropHint);
+                    estimateCrop.scale(1f / options.inSampleSize);
+                    final float hRatio = (float) wpData.mHeight / estimateCrop.height();
+                    final int destHeight = (int) (estimateCrop.height() * hRatio);
+                    final int destWidth = (int) (estimateCrop.width() * hRatio);
+
+                    // We estimated an invalid crop, try to adjust the cropHint to get a valid one.
+                    if (destWidth > GLHelper.getMaxTextureSize()) {
+                        int newHeight = (int) (wpData.mHeight / hRatio);
+                        int newWidth = (int) (wpData.mWidth / hRatio);
+
+                        if (DEBUG) {
+                            Slog.v(TAG, "Invalid crop dimensions, trying to adjust.");
+                        }
+
+                        estimateCrop.set(cropHint);
+                        estimateCrop.left += (cropHint.width() - newWidth) / 2;
+                        estimateCrop.top += (cropHint.height() - newHeight) / 2;
+                        estimateCrop.right = estimateCrop.left + newWidth;
+                        estimateCrop.bottom = estimateCrop.top + newHeight;
+                        cropHint.set(estimateCrop);
+                        estimateCrop.scale(1f / options.inSampleSize);
+                    }
+
+                    // We've got the safe cropHint; now we want to scale it properly to
+                    // the desired rectangle.
+                    // That's a height-biased operation: make it fit the hinted height.
+                    final int safeHeight = (int) (estimateCrop.height() * hRatio);
+                    final int safeWidth = (int) (estimateCrop.width() * hRatio);
+
+                    if (DEBUG_CROP) {
+                        Slog.v(TAG, "Decode parameters:");
+                        Slog.v(TAG, "  cropHint=" + cropHint + ", estimateCrop=" + estimateCrop);
+                        Slog.v(TAG, "  down sampling=" + options.inSampleSize
+                                + ", hRatio=" + hRatio);
+                        Slog.v(TAG, "  dest=" + destWidth + "x" + destHeight);
+                        Slog.v(TAG, "  safe=" + safeWidth + "x" + safeHeight);
+                        Slog.v(TAG, "  maxTextureSize=" + GLHelper.getMaxTextureSize());
+                    }
+
+                    //Create a record file and will delete if ImageDecoder work well.
+                    final String recordName =
+                            (wallpaper.wallpaperFile.getName().equals(WALLPAPER)
+                                    ? RECORD_FILE : RECORD_LOCK_FILE);
+                    final File record = new File(getWallpaperDir(wallpaper.userId), recordName);
+                    record.createNewFile();
+                    Slog.v(TAG, "record path =" + record.getPath()
+                            + ", record name =" + record.getName());
+
+                    final ImageDecoder.Source srcData =
+                            ImageDecoder.createSource(wallpaper.wallpaperFile);
+                    final int sampleSize = scale;
+                    Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> {
+                        decoder.setTargetSampleSize(sampleSize);
+                        decoder.setCrop(estimateCrop);
+                    });
+
+                    record.delete();
+
+                    if (cropped == null) {
+                        Slog.e(TAG, "Could not decode new wallpaper");
+                    } else {
+                        // We are safe to create final crop with safe dimensions now.
+                        final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
+                                safeWidth, safeHeight, true);
+                        if (DEBUG) {
+                            Slog.v(TAG, "Final extract:");
+                            Slog.v(TAG, "  dims: w=" + wpData.mWidth
+                                    + " h=" + wpData.mHeight);
+                            Slog.v(TAG, "  out: w=" + finalCrop.getWidth()
+                                    + " h=" + finalCrop.getHeight());
+                        }
+
+                        f = new FileOutputStream(wallpaper.cropFile);
+                        bos = new BufferedOutputStream(f, 32 * 1024);
+                        finalCrop.compress(Bitmap.CompressFormat.PNG, 100, bos);
+                        // don't rely on the implicit flush-at-close when noting success
+                        bos.flush();
+                        success = true;
+                    }
+                } catch (Exception e) {
+                    if (DEBUG) {
+                        Slog.e(TAG, "Error decoding crop", e);
+                    }
+                } finally {
+                    IoUtils.closeQuietly(bos);
+                    IoUtils.closeQuietly(f);
+                }
+            }
+        }
+
+        if (!success) {
+            Slog.e(TAG, "Unable to apply new wallpaper");
+            wallpaper.cropFile.delete();
+        }
+
+        if (wallpaper.cropFile.exists()) {
+            boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
+            if (DEBUG) {
+                Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
index a380dea..f02ee66 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
@@ -27,7 +27,6 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.util.function.Consumer;
@@ -36,7 +35,6 @@
  */
 class WallpaperDisplayHelper {
 
-    @VisibleForTesting
     static final class DisplayData {
         int mWidth = -1;
         int mHeight = -1;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index b146767..bf09b67 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -74,7 +74,6 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Color;
-import android.graphics.ImageDecoder;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.hardware.display.DisplayManager;
@@ -109,7 +108,6 @@
 import android.util.SparseBooleanArray;
 import android.util.Xml;
 import android.view.Display;
-import android.view.DisplayInfo;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -132,7 +130,6 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -308,7 +305,7 @@
                                 }
                                 loadSettingsLocked(wallpaper.userId, true);
                             }
-                            generateCrop(wallpaper);
+                            mWallpaperCropper.generateCrop(wallpaper);
                             if (DEBUG) {
                                 Slog.v(TAG, "Crop done; invoking completion callback");
                             }
@@ -593,233 +590,6 @@
         return colors;
     }
 
-    /**
-     * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
-     * for display.
-     */
-    void generateCrop(WallpaperData wallpaper) {
-        TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
-        t.traceBegin("WPMS.generateCrop");
-        generateCropInternal(wallpaper);
-        t.traceEnd();
-    }
-
-    private void generateCropInternal(WallpaperData wallpaper) {
-        boolean success = false;
-
-        // Only generate crop for default display.
-        final DisplayData wpData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
-        final Rect cropHint = new Rect(wallpaper.cropHint);
-        final DisplayInfo displayInfo = mWallpaperDisplayHelper.getDisplayInfo(DEFAULT_DISPLAY);
-
-        if (DEBUG) {
-            Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
-                    + Integer.toHexString(wallpaper.mWhich)
-                    + " to " + wallpaper.cropFile.getName()
-                    + " crop=(" + cropHint.width() + 'x' + cropHint.height()
-                    + ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')');
-        }
-
-        // Analyse the source; needed in multiple cases
-        BitmapFactory.Options options = new BitmapFactory.Options();
-        options.inJustDecodeBounds = true;
-        BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
-        if (options.outWidth <= 0 || options.outHeight <= 0) {
-            Slog.w(TAG, "Invalid wallpaper data");
-            success = false;
-        } else {
-            boolean needCrop = false;
-            boolean needScale = false;
-
-            // Empty crop means use the full image
-            if (cropHint.isEmpty()) {
-                cropHint.left = cropHint.top = 0;
-                cropHint.right = options.outWidth;
-                cropHint.bottom = options.outHeight;
-            } else {
-                // force the crop rect to lie within the measured bounds
-                cropHint.offset(
-                        (cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0),
-                        (cropHint.bottom > options.outHeight ? options.outHeight - cropHint.bottom : 0));
-
-                // If the crop hint was larger than the image we just overshot. Patch things up.
-                if (cropHint.left < 0) {
-                    cropHint.left = 0;
-                }
-                if (cropHint.top < 0) {
-                    cropHint.top = 0;
-                }
-
-                // Don't bother cropping if what we're left with is identity
-                needCrop = (options.outHeight > cropHint.height()
-                        || options.outWidth > cropHint.width());
-            }
-
-            // scale if the crop height winds up not matching the recommended metrics
-            needScale = cropHint.height() > wpData.mHeight
-                    || cropHint.height() > GLHelper.getMaxTextureSize()
-                    || cropHint.width() > GLHelper.getMaxTextureSize();
-
-            //make sure screen aspect ratio is preserved if width is scaled under screen size
-            if (needScale) {
-                final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height();
-                final int newWidth = (int) (cropHint.width() * scaleByHeight);
-                if (newWidth < displayInfo.logicalWidth) {
-                    final float screenAspectRatio =
-                            (float) displayInfo.logicalHeight / (float) displayInfo.logicalWidth;
-                    cropHint.bottom = (int) (cropHint.width() * screenAspectRatio);
-                    needCrop = true;
-                }
-            }
-
-            if (DEBUG_CROP) {
-                Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
-                Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
-                Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
-                Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
-            }
-
-            if (!needCrop && !needScale) {
-                // Simple case:  the nominal crop fits what we want, so we take
-                // the whole thing and just copy the image file directly.
-
-                // TODO: It is not accurate to estimate bitmap size without decoding it,
-                //  may be we can try to remove this optimized way in the future,
-                //  that means, we will always go into the 'else' block.
-
-                success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
-
-                if (!success) {
-                    wallpaper.cropFile.delete();
-                    // TODO: fall back to default wallpaper in this case
-                }
-
-                if (DEBUG) {
-                    long estimateSize = (long) options.outWidth * options.outHeight * 4;
-                    Slog.v(TAG, "Null crop of new wallpaper, estimate size="
-                            + estimateSize + ", success=" + success);
-                }
-            } else {
-                // Fancy case: crop and scale.  First, we decode and scale down if appropriate.
-                FileOutputStream f = null;
-                BufferedOutputStream bos = null;
-                try {
-                    // This actually downsamples only by powers of two, but that's okay; we do
-                    // a proper scaling blit later.  This is to minimize transient RAM use.
-                    // We calculate the largest power-of-two under the actual ratio rather than
-                    // just let the decode take care of it because we also want to remap where the
-                    // cropHint rectangle lies in the decoded [super]rect.
-                    final int actualScale = cropHint.height() / wpData.mHeight;
-                    int scale = 1;
-                    while (2 * scale <= actualScale) {
-                        scale *= 2;
-                    }
-                    options.inSampleSize = scale;
-                    options.inJustDecodeBounds = false;
-
-                    final Rect estimateCrop = new Rect(cropHint);
-                    estimateCrop.scale(1f / options.inSampleSize);
-                    final float hRatio = (float) wpData.mHeight / estimateCrop.height();
-                    final int destHeight = (int) (estimateCrop.height() * hRatio);
-                    final int destWidth = (int) (estimateCrop.width() * hRatio);
-
-                    // We estimated an invalid crop, try to adjust the cropHint to get a valid one.
-                    if (destWidth > GLHelper.getMaxTextureSize()) {
-                        int newHeight = (int) (wpData.mHeight / hRatio);
-                        int newWidth = (int) (wpData.mWidth / hRatio);
-
-                        if (DEBUG) {
-                            Slog.v(TAG, "Invalid crop dimensions, trying to adjust.");
-                        }
-
-                        estimateCrop.set(cropHint);
-                        estimateCrop.left += (cropHint.width() - newWidth) / 2;
-                        estimateCrop.top += (cropHint.height() - newHeight) / 2;
-                        estimateCrop.right = estimateCrop.left + newWidth;
-                        estimateCrop.bottom = estimateCrop.top + newHeight;
-                        cropHint.set(estimateCrop);
-                        estimateCrop.scale(1f / options.inSampleSize);
-                    }
-
-                    // We've got the safe cropHint; now we want to scale it properly to
-                    // the desired rectangle.
-                    // That's a height-biased operation: make it fit the hinted height.
-                    final int safeHeight = (int) (estimateCrop.height() * hRatio);
-                    final int safeWidth = (int) (estimateCrop.width() * hRatio);
-
-                    if (DEBUG_CROP) {
-                        Slog.v(TAG, "Decode parameters:");
-                        Slog.v(TAG, "  cropHint=" + cropHint + ", estimateCrop=" + estimateCrop);
-                        Slog.v(TAG, "  down sampling=" + options.inSampleSize
-                                + ", hRatio=" + hRatio);
-                        Slog.v(TAG, "  dest=" + destWidth + "x" + destHeight);
-                        Slog.v(TAG, "  safe=" + safeWidth + "x" + safeHeight);
-                        Slog.v(TAG, "  maxTextureSize=" + GLHelper.getMaxTextureSize());
-                    }
-
-                    //Create a record file and will delete if ImageDecoder work well.
-                    final String recordName =
-                            (wallpaper.wallpaperFile.getName().equals(WALLPAPER)
-                                    ? RECORD_FILE : RECORD_LOCK_FILE);
-                    final File record = new File(getWallpaperDir(wallpaper.userId), recordName);
-                    record.createNewFile();
-                    Slog.v(TAG, "record path =" + record.getPath()
-                            + ", record name =" + record.getName());
-
-                    final ImageDecoder.Source srcData =
-                            ImageDecoder.createSource(wallpaper.wallpaperFile);
-                    final int sampleSize = scale;
-                    Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> {
-                        decoder.setTargetSampleSize(sampleSize);
-                        decoder.setCrop(estimateCrop);
-                    });
-
-                    record.delete();
-
-                    if (cropped == null) {
-                        Slog.e(TAG, "Could not decode new wallpaper");
-                    } else {
-                        // We are safe to create final crop with safe dimensions now.
-                        final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
-                                safeWidth, safeHeight, true);
-                        if (DEBUG) {
-                            Slog.v(TAG, "Final extract:");
-                            Slog.v(TAG, "  dims: w=" + wpData.mWidth
-                                    + " h=" + wpData.mHeight);
-                            Slog.v(TAG, "  out: w=" + finalCrop.getWidth()
-                                    + " h=" + finalCrop.getHeight());
-                        }
-
-                        f = new FileOutputStream(wallpaper.cropFile);
-                        bos = new BufferedOutputStream(f, 32*1024);
-                        finalCrop.compress(Bitmap.CompressFormat.PNG, 100, bos);
-                        bos.flush();  // don't rely on the implicit flush-at-close when noting success
-                        success = true;
-                    }
-                } catch (Exception e) {
-                    if (DEBUG) {
-                        Slog.e(TAG, "Error decoding crop", e);
-                    }
-                } finally {
-                    IoUtils.closeQuietly(bos);
-                    IoUtils.closeQuietly(f);
-                }
-            }
-        }
-
-        if (!success) {
-            Slog.e(TAG, "Unable to apply new wallpaper");
-            wallpaper.cropFile.delete();
-        }
-
-        if (wallpaper.cropFile.exists()) {
-            boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
-            if (DEBUG) {
-                Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
-            }
-        }
-    }
-
     private final Context mContext;
     private final WindowManagerInternal mWindowManagerInternal;
     private final IPackageManager mIPackageManager;
@@ -912,6 +682,7 @@
 
     @VisibleForTesting
     final WallpaperDisplayHelper mWallpaperDisplayHelper;
+    final WallpaperCropper mWallpaperCropper;
 
     private boolean supportsMultiDisplay(WallpaperConnection connection) {
         if (connection != null) {
@@ -1567,6 +1338,7 @@
         DisplayManager dm = mContext.getSystemService(DisplayManager.class);
         dm.registerDisplayListener(mDisplayListener, null /* handler */);
         mWallpaperDisplayHelper = new WallpaperDisplayHelper(dm, mWindowManagerInternal);
+        mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
         mActivityManager = mContext.getSystemService(ActivityManager.class);
         mMonitor = new MyPackageMonitor();
         mColorsChangedListeners = new SparseArray<>();
@@ -1617,7 +1389,7 @@
                 if (DEBUG) {
                     Slog.i(TAG, "No crop; regenerating from source");
                 }
-                generateCrop(wallpaper);
+                mWallpaperCropper.generateCrop(wallpaper);
             }
             // Still nothing?  Fall back to default.
             if (!wallpaper.cropExists()) {
@@ -3479,7 +3251,7 @@
             mWallpaperMap.put(userId, wallpaper);
             if (!wallpaper.cropExists()) {
                 if (wallpaper.sourceExists()) {
-                    generateCrop(wallpaper);
+                    mWallpaperCropper.generateCrop(wallpaper);
                 } else {
                     Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
                 }
@@ -3700,7 +3472,7 @@
                 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
                         + " id=" + wallpaper.wallpaperId);
                 if (success) {
-                    generateCrop(wallpaper); // based on the new image + metadata
+                    mWallpaperCropper.generateCrop(wallpaper); // based on the new image + metadata
                     bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
                             wallpaper, null);
                 }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index d65c2f9..2b8b59c 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -901,9 +901,18 @@
      * TODO(b/213312721): Remove this predicate and its callers once ShellTransition is enabled.
      */
     static boolean isTaskViewTask(WindowContainer wc) {
-        // We use Task#mRemoveWithTaskOrganizer to identify an embedded Task, but this is a hack and
+        // Use Task#mRemoveWithTaskOrganizer to identify an embedded Task, but this is a hack and
         // it is not guaranteed to work this logic in the future version.
-        return wc instanceof Task && ((Task) wc).mRemoveWithTaskOrganizer;
+        boolean isTaskViewTask =  wc instanceof Task && ((Task) wc).mRemoveWithTaskOrganizer;
+        if (isTaskViewTask) {
+            return true;
+        }
+
+        WindowContainer parent = wc.getParent();
+        boolean isParentATaskViewTask = parent != null
+                && parent instanceof Task
+                && ((Task) parent).mRemoveWithTaskOrganizer;
+        return isParentATaskViewTask;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0bd59a8..1794e2a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5264,6 +5264,11 @@
             return b;
         }
 
+        // WARNING: it says `mSurfaceControl` below, but this CHANGES meaning after construction!
+        // DisplayAreas are added in `configureSurface()` *before* `mSurfaceControl` gets replaced
+        // with a wrapper or magnification surface so they end up in the right place; however,
+        // anything added or reparented to "the display" *afterwards* needs to be reparented to
+        // `getWindowinglayer()` (unless it's an overlay DisplayArea).
         return b.setName(child.getName())
                 .setParent(mSurfaceControl);
     }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a76d836..1b59d8d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3271,12 +3271,17 @@
         // We intend to let organizer manage task visibility but it doesn't
         // have enough information until we finish shell transitions.
         // In the mean time we do an easy fix here.
-        final boolean show = isVisible() || isAnimating(TRANSITION | PARENTS | CHILDREN);
+        final boolean visible = isVisible();
+        final boolean show = visible || isAnimating(TRANSITION | PARENTS | CHILDREN);
         if (mSurfaceControl != null) {
             if (show != mLastSurfaceShowing) {
                 t.setVisibility(mSurfaceControl, show);
             }
         }
+        // Only show the overlay if the task has other visible children
+        if (mOverlayHost != null) {
+            mOverlayHost.setVisibility(t, visible);
+        }
         mLastSurfaceShowing = show;
     }
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 7d3367f..7b56b0c 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1650,6 +1650,12 @@
             // DisplayContent is the "root", so we reinterpret it's wc as the window layer
             // making the parent surface the displaycontent's surface.
             return wc.getSurfaceControl();
+        } else if (wc.getParent().asDisplayContent() != null) {
+            // DisplayContent is kinda split into 2 pieces, the "real root" and the
+            // "windowing layer". So if the parent of the window is DC, then it really belongs on
+            // the windowing layer (unless it's an overlay display area, but those can't be in
+            // transitions anyways).
+            return wc.getParent().asDisplayContent().getWindowingLayer();
         }
         return wc.getParent().getSurfaceControl();
     }
diff --git a/services/core/java/com/android/server/wm/TrustedOverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
index 975b21c..88c410b 100644
--- a/services/core/java/com/android/server/wm/TrustedOverlayHost.java
+++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
@@ -80,6 +80,12 @@
         }
     }
 
+    void setVisibility(SurfaceControl.Transaction t, boolean visible) {
+        if (mSurfaceControl != null) {
+            t.setVisibility(mSurfaceControl, visible);
+        }
+    }
+
     void addOverlay(SurfaceControlViewHost.SurfacePackage p, SurfaceControl currentParent) {
         requireOverlaySurfaceControl();
         mOverlays.add(p);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8767096..bded45e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3174,7 +3174,8 @@
 
     /**
      * Moves the given display to the top. If it cannot be moved to the top this method does
-     * nothing.
+     * nothing (e.g. if the display has the flag FLAG_STEAL_TOP_FOCUS_DISABLED set).
+     * @param displayId The display to move to the top.
      */
     void moveDisplayToTopInternal(int displayId) {
         synchronized (mGlobalLock) {
@@ -3189,14 +3190,6 @@
                     return;
                 }
 
-                if (mPerDisplayFocusEnabled) {
-                    ProtoLog.i(WM_DEBUG_FOCUS_LIGHT,
-                            "Not moving display (displayId=%d) to top. Top focused displayId=%d. "
-                                    + "Reason: config_perDisplayFocusEnabled", displayId,
-                            mRoot.getTopFocusedDisplayContent().getDisplayId());
-                    return;
-                }
-
                 // Nothing prevented us from moving the display to the top. Let's do it!
                 displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP,
                         displayContent, true /* includingParents */);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 223352e..33af563 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -6241,7 +6241,6 @@
 
     @Override
     public void handleTapOutsideFocusInsideSelf() {
-        final DisplayContent displayContent = getDisplayContent();
         mWmService.moveDisplayToTopInternal(getDisplayId());
         mWmService.handleTaskFocusChange(getTask(), mActivityRecord);
     }
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index d3d69ae..4af685e 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -146,6 +146,7 @@
 // vmas vector, instead it iterates on it until the end.
 class VmaBatchCreator {
     const std::vector<Vma>* sourceVmas;
+    const int totalVmasInSource;
     // This is the destination array where batched VMAs will be stored
     // it gets encapsulated into a VmaBatch which is the object
     // meant to be used by client code.
@@ -156,8 +157,13 @@
     uint64_t currentOffset_;
 
 public:
-    VmaBatchCreator(const std::vector<Vma>* vmasToBatch, struct iovec* destVmasVec)
-          : sourceVmas(vmasToBatch), destVmas(destVmasVec), currentIndex_(0), currentOffset_(0) {}
+    VmaBatchCreator(const std::vector<Vma>* vmasToBatch, struct iovec* destVmasVec,
+                    int vmasInSource)
+          : sourceVmas(vmasToBatch),
+            totalVmasInSource(vmasInSource),
+            destVmas(destVmasVec),
+            currentIndex_(0),
+            currentOffset_(0) {}
 
     int currentIndex() { return currentIndex_; }
     uint64_t currentOffset() { return currentOffset_; }
@@ -177,7 +183,7 @@
 
         // Add VMAs to the batch up until we consumed all the VMAs or
         // reached any imposed limit of VMAs per batch.
-        while (indexInBatch < MAX_VMAS_PER_BATCH && currentIndex_ < vmas.size()) {
+        while (indexInBatch < MAX_VMAS_PER_BATCH && currentIndex_ < totalVmasInSource) {
             uint64_t vmaStart = vmas[currentIndex_].start + currentOffset_;
             uint64_t vmaSize = vmas[currentIndex_].end - vmaStart;
             uint64_t bytesAvailableInBatch = MAX_BYTES_PER_BATCH - totalBytesInBatch;
@@ -272,8 +278,9 @@
 //
 // If any VMA fails compaction due to -EINVAL it will be skipped and continue.
 // However, if it fails for any other reason, it will bail out and forward the error
-static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) {
-    if (vmas.empty()) {
+static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType,
+                             int totalVmas) {
+    if (totalVmas == 0) {
         return 0;
     }
 
@@ -286,7 +293,7 @@
     struct iovec destVmas[MAX_VMAS_PER_BATCH];
 
     VmaBatch batch;
-    VmaBatchCreator batcher(&vmas, destVmas);
+    VmaBatchCreator batcher(&vmas, destVmas, totalVmas);
 
     int64_t totalBytesProcessed = 0;
     while (batcher.createNextBatch(batch)) {
@@ -346,34 +353,53 @@
 // Returns the total number of bytes compacted on success. On error
 // returns process_madvise errno code or if compaction was cancelled
 // it returns ERROR_COMPACTION_CANCELLED.
+//
+// Not thread safe. We reuse vectors so we assume this is called only
+// on one thread at most.
 static int64_t compactProcess(int pid, VmaToAdviseFunc vmaToAdviseFunc) {
     cancelRunningCompaction.store(false);
-
+    static std::string mapsBuffer;
     ATRACE_BEGIN("CollectVmas");
     ProcMemInfo meminfo(pid);
-    std::vector<Vma> pageoutVmas, coldVmas;
-    auto vmaCollectorCb = [&coldVmas,&pageoutVmas,&vmaToAdviseFunc](const Vma& vma) {
+    static std::vector<Vma> pageoutVmas(2000), coldVmas(2000);
+    int coldVmaIndex = 0;
+    int pageoutVmaIndex = 0;
+    auto vmaCollectorCb = [&vmaToAdviseFunc, &pageoutVmaIndex, &coldVmaIndex](const Vma& vma) {
         int advice = vmaToAdviseFunc(vma);
         switch (advice) {
             case MADV_COLD:
-                coldVmas.push_back(vma);
+                if (coldVmaIndex < coldVmas.size()) {
+                    coldVmas[coldVmaIndex] = vma;
+                } else {
+                    coldVmas.push_back(vma);
+                }
+                ++coldVmaIndex;
                 break;
             case MADV_PAGEOUT:
-                pageoutVmas.push_back(vma);
+                if (pageoutVmaIndex < pageoutVmas.size()) {
+                    pageoutVmas[pageoutVmaIndex] = vma;
+                } else {
+                    pageoutVmas.push_back(vma);
+                }
+                ++pageoutVmaIndex;
                 break;
         }
     };
-    meminfo.ForEachVmaFromMaps(vmaCollectorCb);
+    meminfo.ForEachVmaFromMaps(vmaCollectorCb, mapsBuffer);
     ATRACE_END();
+#ifdef DEBUG_COMPACTION
+    ALOGE("Total VMAs sent for compaction anon=%d file=%d", pageoutVmaIndex,
+            coldVmaIndex);
+#endif
 
-    int64_t pageoutBytes = compactMemory(pageoutVmas, pid, MADV_PAGEOUT);
+    int64_t pageoutBytes = compactMemory(pageoutVmas, pid, MADV_PAGEOUT, pageoutVmaIndex);
     if (pageoutBytes < 0) {
         // Error, just forward it.
         cancelRunningCompaction.store(false);
         return pageoutBytes;
     }
 
-    int64_t coldBytes = compactMemory(coldVmas, pid, MADV_COLD);
+    int64_t coldBytes = compactMemory(coldVmas, pid, MADV_COLD, coldVmaIndex);
     if (coldBytes < 0) {
         // Error, just forward it.
         cancelRunningCompaction.store(false);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
deleted file mode 100644
index 834f65f..0000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2017 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.devicepolicy;
-
-import android.accounts.Account;
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-import android.app.admin.DevicePolicyDrawableResource;
-import android.app.admin.DevicePolicySafetyChecker;
-import android.app.admin.DevicePolicyStringResource;
-import android.app.admin.FullyManagedDeviceProvisioningParams;
-import android.app.admin.IDevicePolicyManager;
-import android.app.admin.ManagedProfileProvisioningParams;
-import android.app.admin.ParcelableResource;
-import android.content.ComponentName;
-import android.os.UserHandle;
-import android.util.Slog;
-
-import com.android.server.SystemService;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Defines the required interface for IDevicePolicyManager implemenation.
- *
- * <p>The interface consists of public parts determined by {@link IDevicePolicyManager} and also
- * several package private methods required by internal infrastructure.
- *
- * <p>Whenever adding an AIDL method to {@link IDevicePolicyManager}, an empty override method
- * should be added here to avoid build breakage in downstream branches.
- */
-abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
-
-    private static final String TAG = BaseIDevicePolicyManager.class.getSimpleName();
-
-    /**
-     * To be called by {@link DevicePolicyManagerService#Lifecycle} during the various boot phases.
-     *
-     * @see {@link SystemService#onBootPhase}.
-     */
-    abstract void systemReady(int phase);
-    /**
-     * To be called by {@link DevicePolicyManagerService#Lifecycle} when a new user starts.
-     *
-     * @see {@link SystemService#onUserStarting}
-     */
-    abstract void handleStartUser(int userId);
-    /**
-     * To be called by {@link DevicePolicyManagerService#Lifecycle} when a user is being unlocked.
-     *
-     * @see {@link SystemService#onUserUnlocking}
-     */
-    abstract void handleUnlockUser(int userId);
-    /**
-     * To be called by {@link DevicePolicyManagerService#Lifecycle} after a user is being unlocked.
-     *
-     * @see {@link SystemService#onUserUnlocked}
-     */
-    abstract void handleOnUserUnlocked(int userId);
-    /**
-     * To be called by {@link DevicePolicyManagerService#Lifecycle} when a user is being stopped.
-     *
-     * @see {@link SystemService#onUserStopping}
-     */
-    abstract void handleStopUser(int userId);
-
-    /**
-     * Sets the {@link DevicePolicySafetyChecker}.
-     *
-     * <p>Currently, it's called only by {@code SystemServer} on
-     * {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE automotive builds}
-     */
-    public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
-        Slog.w(TAG, "setDevicePolicySafetyChecker() not implemented by " + getClass());
-    }
-
-    public void clearSystemUpdatePolicyFreezePeriodRecord() {
-    }
-
-    public boolean setKeyGrantForApp(ComponentName admin, String callerPackage, String alias,
-            String packageName, boolean hasGrant) {
-        return false;
-    }
-
-    public void setLocationEnabled(ComponentName who, boolean locationEnabled) {}
-
-    public boolean isOrganizationOwnedDeviceWithManagedProfile() {
-        return false;
-    }
-
-    public int getPersonalAppsSuspendedReasons(ComponentName admin) {
-        return 0;
-    }
-
-    public void setPersonalAppsSuspended(ComponentName admin, boolean suspended) {
-    }
-
-    public void setManagedProfileMaximumTimeOff(ComponentName admin, long timeoutMs) {
-    }
-
-    public long getManagedProfileMaximumTimeOff(ComponentName admin) {
-        return 0;
-    }
-
-    @Override
-    public void acknowledgeDeviceCompliant() {}
-
-    @Override
-    public boolean isComplianceAcknowledgementRequired() {
-        return false;
-    }
-
-    public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
-        return false;
-    }
-
-    public String getEnrollmentSpecificId(String callerPackage) {
-        return "";
-    }
-
-    public void setOrganizationIdForUser(
-            @NonNull String callerPackage, @NonNull String enterpriseId, int userId) {}
-
-    public UserHandle createAndProvisionManagedProfile(
-            @NonNull ManagedProfileProvisioningParams provisioningParams, String callerPackage) {
-        return null;
-    }
-
-    public void finalizeWorkProfileProvisioning(
-            UserHandle managedProfileUser, Account migratedAccount) {
-
-    }
-
-    public void provisionFullyManagedDevice(
-            FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
-    }
-
-    @Override
-    public void setDeviceOwnerType(@NonNull ComponentName admin, int deviceOwnerType) {
-    }
-
-    @Override
-    public int getDeviceOwnerType(@NonNull ComponentName admin) {
-        return 0;
-    }
-
-    public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {}
-
-    public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
-        return false;
-    }
-
-    @Override
-    public boolean setKeyGrantToWifiAuth(String callerPackage, String alias, boolean hasGrant) {
-        return false;
-    }
-
-    @Override
-    public boolean isKeyPairGrantedToWifiAuth(String callerPackage, String alias) {
-        return false;
-    }
-
-    @Override
-    public void setDrawables(@NonNull List<DevicePolicyDrawableResource> drawables){}
-
-    @Override
-    public void resetDrawables(@NonNull List<String> drawableIds){}
-
-    @Override
-    public ParcelableResource getDrawable(
-            String drawableId, String drawableStyle, String drawableSource) {
-        return null;
-    }
-
-    @Override
-    public void setStrings(@NonNull List<DevicePolicyStringResource> strings){}
-
-    @Override
-    public void resetStrings(@NonNull List<String> stringIds){}
-
-    @Override
-    public ParcelableResource getString(String stringId) {
-        return null;
-    }
-
-    @Override
-    public boolean shouldAllowBypassingDevicePolicyManagementRoleQualification() {
-        return false;
-    }
-
-    @Override
-    public List<UserHandle> getPolicyManagedProfiles(UserHandle userHandle) {
-        return Collections.emptyList();
-    }
-}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1406a396..3470b04 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -221,6 +221,7 @@
 import android.app.admin.DeviceStateCache;
 import android.app.admin.FactoryResetProtectionPolicy;
 import android.app.admin.FullyManagedDeviceProvisioningParams;
+import android.app.admin.IDevicePolicyManager;
 import android.app.admin.ManagedProfileProvisioningParams;
 import android.app.admin.ManagedSubscriptionsPolicy;
 import android.app.admin.NetworkEvent;
@@ -442,7 +443,7 @@
 /**
  * Implementation of the device policy APIs.
  */
-public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
 
     protected static final String LOG_TAG = "DevicePolicyManager";
 
@@ -739,6 +740,10 @@
     private static final String KEEP_PROFILES_RUNNING_FLAG = "enable_keep_profiles_running";
     private static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = false;
 
+    private static final String ENABLE_WORK_PROFILE_TELEPHONY_FLAG =
+            "enable_work_profile_telephony";
+    private static final boolean DEFAULT_WORK_PROFILE_TELEPHONY_FLAG = false;
+
     // TODO(b/261999445) remove the flag after rollout.
     private static final String HEADLESS_FLAG = "headless";
     private static final boolean DEFAULT_HEADLESS_FLAG = true;
@@ -920,23 +925,24 @@
     private final ArrayList<Object> mPendingUserCreatedCallbackTokens = new ArrayList<>();
 
     public static final class Lifecycle extends SystemService {
-        private BaseIDevicePolicyManager mService;
+        private DevicePolicyManagerService mService;
 
         public Lifecycle(Context context) {
             super(context);
             String dpmsClassName = context.getResources()
                     .getString(R.string.config_deviceSpecificDevicePolicyManagerService);
             if (TextUtils.isEmpty(dpmsClassName)) {
-                dpmsClassName = DevicePolicyManagerService.class.getName();
-            }
-            try {
-                Class<?> serviceClass = Class.forName(dpmsClassName);
-                Constructor<?> constructor = serviceClass.getConstructor(Context.class);
-                mService = (BaseIDevicePolicyManager) constructor.newInstance(context);
-            } catch (Exception e) {
-                throw new IllegalStateException(
-                    "Failed to instantiate DevicePolicyManagerService with class name: "
-                    + dpmsClassName, e);
+                mService = new DevicePolicyManagerService(context);
+            } else {
+                try {
+                    Class<?> serviceClass = Class.forName(dpmsClassName);
+                    Constructor<?> constructor = serviceClass.getConstructor(Context.class);
+                    mService = (DevicePolicyManagerService) constructor.newInstance(context);
+                } catch (Exception e) {
+                    throw new IllegalStateException(
+                        "Failed to instantiate DevicePolicyManagerService with class name: "
+                        + dpmsClassName, e);
+                }
             }
         }
 
@@ -1095,13 +1101,13 @@
                 // (ACTION_DATE_CHANGED), or when manual clock adjustment is made
                 // (ACTION_TIME_CHANGED)
                 updateSystemUpdateFreezePeriodsRecord(/* saveIfChanged */ true);
-                final int userId = getManagedUserId(mUserManager.getMainUser().getIdentifier());
+                final int userId = getManagedUserId(getMainUserId());
                 if (userId >= 0) {
                     updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId));
                 }
             } else if (ACTION_PROFILE_OFF_DEADLINE.equals(action)) {
                 Slogf.i(LOG_TAG, "Profile off deadline alarm was triggered");
-                final int userId = getManagedUserId(mUserManager.getMainUser().getIdentifier());
+                final int userId = getManagedUserId(getMainUserId());
                 if (userId >= 0) {
                     updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId));
                 } else {
@@ -1348,7 +1354,6 @@
         }
     }
 
-    @Override
     public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
         CallerIdentity callerIdentity = getCallerIdentity();
         Preconditions.checkCallAuthorization(mIsAutomotive || isAdb(callerIdentity), "can only set "
@@ -3090,7 +3095,6 @@
     }
 
     @VisibleForTesting
-    @Override
     void systemReady(int phase) {
         if (!mHasFeature) {
             return;
@@ -3100,7 +3104,9 @@
                 onLockSettingsReady();
                 loadAdminDataAsync();
                 mOwners.systemReady();
-                applyManagedSubscriptionsPolicyIfRequired();
+                if (isWorkProfileTelephonyFlagEnabled()) {
+                    applyManagedSubscriptionsPolicyIfRequired();
+                }
                 break;
             case SystemService.PHASE_ACTIVITY_MANAGER_READY:
                 synchronized (getLockObject()) {
@@ -3289,7 +3295,6 @@
         }
     }
 
-    @Override
     void handleStartUser(int userId) {
         synchronized (getLockObject()) {
             pushScreenCapturePolicy(userId);
@@ -3337,7 +3342,6 @@
                         targetUserId, protectedPackages));
     }
 
-    @Override
     void handleUnlockUser(int userId) {
         startOwnerService(userId, "unlock-user");
         if (isCoexistenceFlagEnabled()) {
@@ -3345,12 +3349,10 @@
         }
     }
 
-    @Override
     void handleOnUserUnlocked(int userId) {
         showNewUserDisclaimerIfNecessary(userId);
     }
 
-    @Override
     void handleStopUser(int userId) {
         updateNetworkPreferenceForUser(userId, List.of(PreferentialNetworkServiceConfig.DEFAULT));
         mDeviceAdminServiceController.stopServicesForUser(userId, /* actionForLog= */ "stop-user");
@@ -7018,8 +7020,9 @@
         }
         mLockSettingsInternal.refreshStrongAuthTimeout(parentId);
 
-        clearManagedSubscriptionsPolicy();
-
+        if (isWorkProfileTelephonyFlagEnabled()) {
+            clearManagedSubscriptionsPolicy();
+        }
         Slogf.i(LOG_TAG, "Cleaning up device-wide policies done.");
     }
 
@@ -8879,6 +8882,15 @@
         }
     }
 
+    private @UserIdInt int getMainUserId() {
+        UserHandle mainUser = mUserManager.getMainUser();
+        if (mainUser == null) {
+            Slogf.d(LOG_TAG, "getMainUserId(): no main user, returning USER_SYSTEM");
+            return UserHandle.USER_SYSTEM;
+        }
+        return mainUser.getIdentifier();
+    }
+
     // TODO(b/240562946): Remove api as owner name is not used.
     /**
      * Returns the "name" of the device owner.  It'll work for non-DO users too, but requires
@@ -10123,6 +10135,9 @@
             synchronized (mSubscriptionsChangedListenerLock) {
                 pw.println("Subscription changed listener : " + mSubscriptionsChangedListener);
             }
+            pw.println(
+                    "Flag enable_work_profile_telephony : " + isWorkProfileTelephonyFlagEnabled());
+
             mHandler.post(() -> handleDump(pw));
             dumpResources(pw);
         }
@@ -20095,6 +20110,13 @@
                 DEFAULT_KEEP_PROFILES_RUNNING_FLAG);
     }
 
+    private static boolean isWorkProfileTelephonyFlagEnabled() {
+        return DeviceConfig.getBoolean(
+                NAMESPACE_DEVICE_POLICY_MANAGER,
+                ENABLE_WORK_PROFILE_TELEPHONY_FLAG,
+                DEFAULT_WORK_PROFILE_TELEPHONY_FLAG);
+    }
+
     @Override
     public void setMtePolicy(int flags) {
         final Set<Integer> allowedModes =
@@ -20175,10 +20197,12 @@
 
     @Override
     public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
-        synchronized (getLockObject()) {
-            ActiveAdmin admin = getProfileOwnerOfOrganizationOwnedDeviceLocked();
-            if (admin != null && admin.mManagedSubscriptionsPolicy != null) {
-                return admin.mManagedSubscriptionsPolicy;
+        if (isWorkProfileTelephonyFlagEnabled()) {
+            synchronized (getLockObject()) {
+                ActiveAdmin admin = getProfileOwnerOfOrganizationOwnedDeviceLocked();
+                if (admin != null && admin.mManagedSubscriptionsPolicy != null) {
+                    return admin.mManagedSubscriptionsPolicy;
+                }
             }
         }
         return new ManagedSubscriptionsPolicy(
@@ -20187,9 +20211,13 @@
 
     @Override
     public void setManagedSubscriptionsPolicy(ManagedSubscriptionsPolicy policy) {
+        if (!isWorkProfileTelephonyFlagEnabled()) {
+            throw new UnsupportedOperationException("This api is not enabled");
+        }
         CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller),
-                "This policy can only be set by a profile owner on an organization-owned device.");
+                "This policy can only be set by a profile owner on an organization-owned "
+                        + "device.");
 
         synchronized (getLockObject()) {
             final ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
@@ -20307,4 +20335,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3725756..a15c6d2 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1021,9 +1021,7 @@
             Runnable runnable = new Runnable() {
                 @Override
                 public void run() {
-                    synchronized (this) {
-                        ShutdownThread.rebootOrShutdown(null, reboot, reason);
-                    }
+                    ShutdownThread.rebootOrShutdown(null, reboot, reason);
                 }
             };
 
@@ -1446,6 +1444,9 @@
         boolean isArc = context.getPackageManager().hasSystemFeature(
                 "org.chromium.arc");
 
+        boolean isTv = context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_LEANBACK);
+
         boolean enableVrService = context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE);
 
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 681bfcf..f915298 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -51,6 +51,7 @@
         "kotlin-test",
         "mockingservicestests-utils-mockito",
         "mockito-target-extended-minus-junit4",
+        "platform-compat-test-rules",
         "platform-test-annotations",
         "service-blobstore",
         "service-jobscheduler",
diff --git a/services/tests/servicestests/res/raw/backup_file_with_long_name b/services/tests/mockingservicestests/res/raw/backup_file_with_long_name
similarity index 100%
rename from services/tests/servicestests/res/raw/backup_file_with_long_name
rename to services/tests/mockingservicestests/res/raw/backup_file_with_long_name
Binary files differ
diff --git a/services/tests/servicestests/res/raw/backup_telephony_no_password b/services/tests/mockingservicestests/res/raw/backup_telephony_no_password
similarity index 100%
rename from services/tests/servicestests/res/raw/backup_telephony_no_password
rename to services/tests/mockingservicestests/res/raw/backup_telephony_no_password
Binary files differ
diff --git a/services/tests/servicestests/res/raw/backup_telephony_with_password b/services/tests/mockingservicestests/res/raw/backup_telephony_with_password
similarity index 100%
rename from services/tests/servicestests/res/raw/backup_telephony_with_password
rename to services/tests/mockingservicestests/res/raw/backup_telephony_with_password
Binary files differ
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 05cad16..64e39ef 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -194,7 +194,7 @@
     public void init_withDeviceConfigSetsParameters() {
         // When the DeviceConfig already has a flag value stored (note this test will need to
         // change if the default value changes from false).
-        assertThat(CachedAppOptimizer.DEFAULT_USE_COMPACTION).isFalse();
+        assertThat(CachedAppOptimizer.DEFAULT_USE_COMPACTION).isTrue();
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CachedAppOptimizer.KEY_USE_COMPACTION, "true", false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -372,9 +372,8 @@
                 CachedAppOptimizer.KEY_USE_COMPACTION, "foobar", false);
         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
 
-        // Then we set the default.
-        assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
-                CachedAppOptimizer.DEFAULT_USE_COMPACTION);
+        // Invalid value is mapped to false
+        assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(false);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupPasswordManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupPasswordManagerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/BackupPasswordManagerTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/BackupPasswordManagerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/DataChangedJournalTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/DataChangedJournalTest.java
index 9daa4ba..4b0b760 100644
--- a/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/DataChangedJournalTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.backup;
diff --git a/services/tests/servicestests/src/com/android/server/backup/ProcessedPackagesJournalTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/ProcessedPackagesJournalTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/ProcessedPackagesJournalTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/ProcessedPackagesJournalTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupPreferencesTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
index 5800aca..2c5ae37 100644
--- a/services/tests/servicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/services/tests/servicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/internal/LifecycleOperationStorageTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/internal/LifecycleOperationStorageTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/internal/LifecycleOperationStorageTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/internal/LifecycleOperationStorageTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/PerformAdbRestoreTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformAdbRestoreTaskTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/backup/restore/PerformAdbRestoreTaskTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformAdbRestoreTaskTest.java
index 00c6391..4d6845c 100644
--- a/services/tests/servicestests/src/com/android/server/backup/restore/PerformAdbRestoreTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformAdbRestoreTaskTest.java
@@ -25,7 +25,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.frameworks.servicestests.R;
+import com.android.frameworks.mockingservicestests.R;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/mockingservicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
index 48b0aad..6093f4b 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
@@ -58,7 +58,7 @@
 @RunWith(AndroidJUnit4.class)
 public class BackupEligibilityRulesTest {
     private static final String CUSTOM_BACKUP_AGENT_NAME = "custom.backup.agent";
-    private static final String TEST_PACKAGE_NAME = "com.android.frameworks.servicestests";
+    private static final String TEST_PACKAGE_NAME = "com.android.frameworks.mockingservicestests";
 
     private static final Signature SIGNATURE_1 = generateSignature((byte) 1);
     private static final Signature SIGNATURE_2 = generateSignature((byte) 2);
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupObserverUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupObserverUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/BackupObserverUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupObserverUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/DataStreamFileCodecTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/DataStreamFileCodecTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/DataStreamFileCodecTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/DataStreamFileCodecTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FileUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/FileUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/FullBackupRestoreObserverUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupRestoreObserverUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/FullBackupRestoreObserverUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupRestoreObserverUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/SparseArrayUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/SparseArrayUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/SparseArrayUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/SparseArrayUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index e2536f8..30c6975 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -51,7 +51,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.frameworks.servicestests.R;
+import com.android.frameworks.mockingservicestests.R;
 import com.android.server.backup.FileMetadata;
 import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.restore.PerformAdbRestoreTask;
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index f2cba40..2a790a1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -80,6 +80,8 @@
     @Mock
     private DisplayBlanker mDisplayBlankerMock;
     @Mock
+    private HighBrightnessModeMetadata mHighBrightnessModeMetadataMock;
+    @Mock
     private LogicalDisplay mLogicalDisplayMock;
     @Mock
     private DisplayDevice mDisplayDeviceMock;
@@ -169,7 +171,7 @@
                 mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
                 mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
                 mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
-        });
+        }, mHighBrightnessModeMetadataMock);
 
         when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
         // send a display power request
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index 4f8cb88..d99ed78 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -84,6 +84,8 @@
     @Mock
     private DisplayDevice mDisplayDeviceMock;
     @Mock
+    private HighBrightnessModeMetadata mHighBrightnessModeMetadataMock;
+    @Mock
     private BrightnessTracker mBrightnessTrackerMock;
     @Mock
     private BrightnessSetting mBrightnessSettingMock;
@@ -151,7 +153,7 @@
                 mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
                 mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
                 mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
-        });
+        }, mHighBrightnessModeMetadataMock);
 
         when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
         // send a display power request
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 19b5ad6..de54537 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -427,7 +427,8 @@
         doReturn(true).when(mService)
                 .bindWallpaperComponentLocked(any(), anyBoolean(), anyBoolean(), any(), any());
         doNothing().when(mService).saveSettingsLocked(wallpaper.userId);
-        doNothing().when(mService).generateCrop(wallpaper);
+        spyOn(mService.mWallpaperCropper);
+        doNothing().when(mService.mWallpaperCropper).generateCrop(wallpaper);
 
         // timestamps of {ACTION_WALLPAPER_CHANGED, onWallpaperColorsChanged}
         final long[] timestamps = new long[2];
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 80305fa..ced2a4b 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -60,7 +60,6 @@
         "truth-prebuilt",
         "junit",
         "junit-params",
-        "platform-compat-test-rules",
         "ActivityContext",
         "coretests-aidl",
     ],
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index 450cc40..908e717 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
@@ -36,6 +36,8 @@
             useParentsContacts='false'
             crossProfileIntentFilterAccessControl='20'
             crossProfileIntentResolutionStrategy='0'
+            mediaSharedWithParent='true'
+            credentialShareableWithParent='false'
         />
     </profile-type>
     <profile-type name='custom.test.1' max-allowed-per-parent='14' />
diff --git a/services/tests/servicestests/src/com/android/server/backup/OWNERS b/services/tests/servicestests/src/com/android/server/backup/OWNERS
deleted file mode 100644
index d99779e..0000000
--- a/services/tests/servicestests/src/com/android/server/backup/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/backup/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java
new file mode 100644
index 0000000..c7fb97f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.datatransfer.contextsync;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.platform.test.annotations.Presubmit;
+import android.telecom.Call;
+import android.telecom.ParcelableCall;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.Set;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+public class CrossDeviceCallTest {
+
+    private static final String CALLER_DISPLAY_NAME = "name";
+    private static final String CONTACT_DISPLAY_NAME = "contact";
+
+    @Test
+    public void updateCallDetails_uninitialized() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+                .isEqualTo(android.companion.Telecom.Call.UNKNOWN_STATUS);
+        assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()).isEmpty();
+    }
+
+    @Test
+    public void updateCallDetails_ringing() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
+                Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+        assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+                .isEqualTo(android.companion.Telecom.Call.RINGING);
+        assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+                .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT,
+                        android.companion.Telecom.Call.REJECT,
+                        android.companion.Telecom.Call.SILENCE));
+    }
+
+    @Test
+    public void updateCallDetails_ongoing() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE,
+                Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+        assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+                .isEqualTo(android.companion.Telecom.Call.ONGOING);
+        assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+                .isEqualTo(Set.of(android.companion.Telecom.Call.END,
+                        android.companion.Telecom.Call.MUTE,
+                        android.companion.Telecom.Call.PUT_ON_HOLD));
+    }
+
+    @Test
+    public void updateCallDetails_holding() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_HOLDING,
+                Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+        assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+                .isEqualTo(android.companion.Telecom.Call.ON_HOLD);
+        assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+                .isEqualTo(Set.of(android.companion.Telecom.Call.END,
+                        android.companion.Telecom.Call.TAKE_OFF_HOLD));
+    }
+
+    @Test
+    public void updateCallDetails_cannotHold() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.updateCallDetails(
+                createCallDetails(Call.STATE_ACTIVE, Call.Details.CAPABILITY_MUTE));
+        assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+                .isEqualTo(android.companion.Telecom.Call.ONGOING);
+        assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+                .isEqualTo(Set.of(android.companion.Telecom.Call.END,
+                        android.companion.Telecom.Call.MUTE));
+    }
+
+    @Test
+    public void updateCallDetails_cannotMute() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.updateCallDetails(
+                createCallDetails(Call.STATE_ACTIVE, Call.Details.CAPABILITY_HOLD));
+        assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+                .isEqualTo(android.companion.Telecom.Call.ONGOING);
+        assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+                .isEqualTo(Set.of(android.companion.Telecom.Call.END,
+                        android.companion.Telecom.Call.PUT_ON_HOLD));
+    }
+
+    @Test
+    public void updateCallDetails_transitionRingingToOngoing() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
+                Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+        assertWithMessage("Wrong status for ringing state").that(crossDeviceCall.getStatus())
+                .isEqualTo(android.companion.Telecom.Call.RINGING);
+        assertWithMessage("Wrong controls for ringing state").that(crossDeviceCall.getControls())
+                .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT,
+                        android.companion.Telecom.Call.REJECT,
+                        android.companion.Telecom.Call.SILENCE));
+        crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE,
+                Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+        assertWithMessage("Wrong status for active state").that(crossDeviceCall.getStatus())
+                .isEqualTo(android.companion.Telecom.Call.ONGOING);
+        assertWithMessage("Wrong controls for active state").that(crossDeviceCall.getControls())
+                .isEqualTo(Set.of(android.companion.Telecom.Call.END,
+                        android.companion.Telecom.Call.MUTE,
+                        android.companion.Telecom.Call.PUT_ON_HOLD));
+    }
+
+    @Test
+    public void updateSilencedIfRinging_ringing_silenced() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
+                Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+        crossDeviceCall.updateSilencedIfRinging();
+        assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+                .isEqualTo(android.companion.Telecom.Call.RINGING_SILENCED);
+        assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+                .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT,
+                        android.companion.Telecom.Call.REJECT));
+    }
+
+    @Test
+    public void updateSilencedIfRinging_notRinging_notSilenced() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE,
+                Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+        crossDeviceCall.updateSilencedIfRinging();
+        assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+                .isEqualTo(android.companion.Telecom.Call.ONGOING);
+        assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+                .isEqualTo(Set.of(android.companion.Telecom.Call.END,
+                        android.companion.Telecom.Call.MUTE,
+                        android.companion.Telecom.Call.PUT_ON_HOLD));
+    }
+
+    @Test
+    public void getReadableCallerId_enterpriseCall_adminBlocked_ott() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.mIsEnterprise = true;
+        crossDeviceCall.mIsOtt = true;
+        crossDeviceCall.updateCallDetails(
+                createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+        final String result = crossDeviceCall.getReadableCallerId(true);
+
+        assertWithMessage("Wrong caller id").that(result)
+                .isEqualTo(CALLER_DISPLAY_NAME);
+    }
+
+    @Test
+    public void getReadableCallerId_enterpriseCall_adminUnblocked_ott() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.mIsEnterprise = true;
+        crossDeviceCall.mIsOtt = true;
+        crossDeviceCall.updateCallDetails(
+                createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+        final String result = crossDeviceCall.getReadableCallerId(false);
+
+        assertWithMessage("Wrong caller id").that(result)
+                .isEqualTo(CALLER_DISPLAY_NAME);
+    }
+
+    @Test
+    public void getReadableCallerId_enterpriseCall_adminBlocked_pstn() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.mIsEnterprise = true;
+        crossDeviceCall.mIsOtt = false;
+        crossDeviceCall.updateCallDetails(
+                createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+        final String result = crossDeviceCall.getReadableCallerId(true);
+
+        assertWithMessage("Wrong caller id").that(result)
+                .isEqualTo(CALLER_DISPLAY_NAME);
+    }
+
+    @Test
+    public void getReadableCallerId_nonEnterpriseCall_adminBlocked_ott() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.mIsEnterprise = false;
+        crossDeviceCall.mIsOtt = true;
+        crossDeviceCall.updateCallDetails(
+                createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+        final String result = crossDeviceCall.getReadableCallerId(true);
+
+        assertWithMessage("Wrong caller id").that(result)
+                .isEqualTo(CALLER_DISPLAY_NAME);
+    }
+
+    @Test
+    public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_ott() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.mIsEnterprise = false;
+        crossDeviceCall.mIsOtt = true;
+        crossDeviceCall.updateCallDetails(
+                createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+        final String result = crossDeviceCall.getReadableCallerId(false);
+
+        assertWithMessage("Wrong caller id").that(result)
+                .isEqualTo(CALLER_DISPLAY_NAME);
+    }
+
+    @Test
+    public void getReadableCallerId_nonEnterpriseCall_adminBlocked_pstn() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.mIsEnterprise = false;
+        crossDeviceCall.mIsOtt = false;
+        crossDeviceCall.updateCallDetails(
+                createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+        final String result = crossDeviceCall.getReadableCallerId(true);
+
+        assertWithMessage("Wrong caller id").that(result)
+                .isEqualTo(CONTACT_DISPLAY_NAME);
+    }
+
+    @Test
+    public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_pstn() {
+        final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+                InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+                null, /* callAudioState= */ null);
+        crossDeviceCall.mIsEnterprise = false;
+        crossDeviceCall.mIsOtt = false;
+        crossDeviceCall.updateCallDetails(
+                createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+        final String result = crossDeviceCall.getReadableCallerId(false);
+
+        assertWithMessage("Wrong caller id").that(result)
+                .isEqualTo(CONTACT_DISPLAY_NAME);
+    }
+
+    private Call.Details createCallDetails(int state, int capabilities) {
+        final ParcelableCall.ParcelableCallBuilder parcelableCallBuilder =
+                new ParcelableCall.ParcelableCallBuilder();
+        parcelableCallBuilder.setCallerDisplayName(CALLER_DISPLAY_NAME);
+        parcelableCallBuilder.setContactDisplayName(CONTACT_DISPLAY_NAME);
+        parcelableCallBuilder.setCapabilities(capabilities);
+        parcelableCallBuilder.setState(state);
+        parcelableCallBuilder.setConferenceableCallIds(Collections.emptyList());
+        return Call.Details.createFromParcelableCall(parcelableCallBuilder.createParcelableCall());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 6a4435f..dad7977 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -634,6 +634,8 @@
 
     @Test
     public void onAppsOnVirtualDeviceChanged_multipleVirtualDevices_listenersNotified() {
+        createVirtualDevice(VIRTUAL_DEVICE_ID_2, DEVICE_OWNER_UID_2);
+
         ArraySet<Integer> uidsOnDevice1 = new ArraySet<>(Arrays.asList(UID_1, UID_2));
         ArraySet<Integer> uidsOnDevice2 = new ArraySet<>(Arrays.asList(UID_3, UID_4));
         mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener);
@@ -645,7 +647,7 @@
                 new ArraySet<>(Arrays.asList(UID_1, UID_2)));
 
         // Notifies that the running apps on the second virtual device has changed.
-        mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId() + 1, uidsOnDevice2);
+        mVdms.notifyRunningAppsChanged(VIRTUAL_DEVICE_ID_2, uidsOnDevice2);
         TestableLooper.get(this).processAllMessages();
         // The union of the apps running on both virtual devices are sent to the listeners.
         verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged(
@@ -1059,6 +1061,16 @@
     }
 
     @Test
+    public void closedDevice_lateCallToRunningAppsChanged_isIgnored() {
+        mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener);
+        int deviceId = mDeviceImpl.getDeviceId();
+        mDeviceImpl.close();
+        mVdms.notifyRunningAppsChanged(deviceId, Sets.newArraySet(UID_1));
+        TestableLooper.get(this).processAllMessages();
+        verify(mAppsOnVirtualDeviceListener, never()).onAppsOnAnyVirtualDeviceChanged(any());
+    }
+
+    @Test
     public void sendKeyEvent_noFd() {
         assertThrows(
                 IllegalArgumentException.class,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 4998a6c..60483f1 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -134,6 +134,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.security.KeyChain;
 import android.security.keystore.AttestationUtils;
@@ -259,6 +260,8 @@
     private static final String PROFILE_OFF_SUSPENSION_TITLE = "suspension_title";
     private static final String PROFILE_OFF_SUSPENSION_TEXT = "suspension_text";
     private static final String PROFILE_OFF_SUSPENSION_SOON_TEXT = "suspension_tomorrow_text";
+    private static final String FLAG_ENABLE_WORK_PROFILE_TELEPHONY =
+            "enable_work_profile_telephony";
 
     @Before
     public void setUp() throws Exception {
@@ -4982,7 +4985,8 @@
     public void testWipeDataManagedProfileOnOrganizationOwnedDevice() throws Exception {
         setupProfileOwner();
         configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
-
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
+                FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "true", false);
         // Even if the caller is the managed profile, the current user is the user 0
         when(getServices().iactivityManager.getCurrentUser())
                 .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
@@ -5043,6 +5047,8 @@
         verify(getServices().packageManagerInternal)
                 .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, UserHandle.USER_SYSTEM);
         verify(getServices().subscriptionManager).setSubscriptionUserHandle(0, null);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
+                FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "false", false);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java b/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java
new file mode 100644
index 0000000..24fc348
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+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;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class HbmEventTest {
+    private long mStartTimeMillis;
+    private long mEndTimeMillis;
+    private HbmEvent mHbmEvent;
+
+    @Before
+    public void setUp() {
+        mStartTimeMillis = 10;
+        mEndTimeMillis = 20;
+        mHbmEvent = new HbmEvent(mStartTimeMillis, mEndTimeMillis);
+    }
+
+    @Test
+    public void getCorrectValues() {
+        assertEquals(mHbmEvent.getStartTimeMillis(), mStartTimeMillis);
+        assertEquals(mHbmEvent.getEndTimeMillis(), mEndTimeMillis);
+    }
+
+    @Test
+    public void toStringGeneratesExpectedString() {
+        String actualString = mHbmEvent.toString();
+        String expectedString = "HbmEvent: {startTimeMillis:" + mStartTimeMillis
+                + ", endTimeMillis: " + mEndTimeMillis + "}, total: "
+                + ((mEndTimeMillis - mStartTimeMillis) / 1000) + "]";
+        assertEquals(actualString, expectedString);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index a1e5ce7..2655c3f 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -96,6 +96,7 @@
     private Binder mDisplayToken;
     private String mDisplayUniqueId;
     private Context mContextSpy;
+    private HighBrightnessModeMetadata mHighBrightnessModeMetadata;
 
     @Rule
     public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@@ -118,6 +119,7 @@
         mTestLooper = new TestLooper(mClock::now);
         mDisplayToken = null;
         mDisplayUniqueId = "unique_id";
+
         mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
         when(mContextSpy.getContentResolver()).thenReturn(resolver);
@@ -134,7 +136,8 @@
         initHandler(null);
         final HighBrightnessModeController hbmc = new HighBrightnessModeController(
                 mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
-                mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
+                mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {},
+                null, mContextSpy);
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
         assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
     }
@@ -144,7 +147,8 @@
         initHandler(null);
         final HighBrightnessModeController hbmc = new HighBrightnessModeController(
                 mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
-                mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
+                mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {},
+                null, mContextSpy);
         hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
@@ -699,9 +703,12 @@
     // Creates instance with standard initialization values.
     private HighBrightnessModeController createDefaultHbm(OffsettableClock clock) {
         initHandler(clock);
+        if (mHighBrightnessModeMetadata == null) {
+            mHighBrightnessModeMetadata = new HighBrightnessModeMetadata();
+        }
         return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
                 DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX,
-                DEFAULT_HBM_DATA, null, () -> {}, mContextSpy);
+                DEFAULT_HBM_DATA, null, () -> {}, mHighBrightnessModeMetadata, mContextSpy);
     }
 
     private void initHandler(OffsettableClock clock) {
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
new file mode 100644
index 0000000..ede54e0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+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;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class HighBrightnessModeMetadataTest {
+    private HighBrightnessModeMetadata mHighBrightnessModeMetadata;
+
+    private long mRunningStartTimeMillis = -1;
+
+    @Before
+    public void setUp() {
+        mHighBrightnessModeMetadata = new HighBrightnessModeMetadata();
+    }
+
+    @Test
+    public void checkDefaultValues() {
+        assertEquals(mHighBrightnessModeMetadata.getRunningStartTimeMillis(),
+                mRunningStartTimeMillis);
+        assertEquals(mHighBrightnessModeMetadata.getHbmEventQueue().size(), 0);
+    }
+
+    @Test
+    public void checkSetValues() {
+        mRunningStartTimeMillis = 10;
+        mHighBrightnessModeMetadata.setRunningStartTimeMillis(mRunningStartTimeMillis);
+        assertEquals(mHighBrightnessModeMetadata.getRunningStartTimeMillis(),
+                mRunningStartTimeMillis);
+        HbmEvent expectedHbmEvent = new HbmEvent(10, 20);
+        mHighBrightnessModeMetadata.addHbmEvent(expectedHbmEvent);
+        HbmEvent actualHbmEvent  = mHighBrightnessModeMetadata.getHbmEventQueue().peekFirst();
+        assertEquals(expectedHbmEvent.toString(), actualHbmEvent.toString());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index cbeaf7b..c24d83f 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -16,18 +16,26 @@
 
 package com.android.server.display.brightness;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.PowerManager;
 import android.view.Display;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.display.BrightnessSetting;
 import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
+import com.android.server.display.brightness.strategy.TemporaryBrightnessStrategy;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -39,11 +47,16 @@
 @RunWith(AndroidJUnit4.class)
 public final class DisplayBrightnessControllerTest {
     private static final int DISPLAY_ID = 1;
+    private static final float DEFAULT_BRIGHTNESS = 0.4f;
 
     @Mock
     private DisplayBrightnessStrategySelector mDisplayBrightnessStrategySelector;
     @Mock
     private Context mContext;
+    @Mock
+    private BrightnessSetting mBrightnessSetting;
+    @Mock
+    private Runnable mOnBrightnessChangeRunnable;
 
     private DisplayBrightnessController mDisplayBrightnessController;
 
@@ -58,11 +71,11 @@
             }
         };
         mDisplayBrightnessController = new DisplayBrightnessController(mContext, injector,
-                DISPLAY_ID);
+                DISPLAY_ID, DEFAULT_BRIGHTNESS, mBrightnessSetting, mOnBrightnessChangeRunnable);
     }
 
     @Test
-    public void updateBrightnessWorksAsExpected() {
+    public void updateBrightness() {
         DisplayPowerRequest displayPowerRequest = mock(DisplayPowerRequest.class);
         DisplayBrightnessStrategy displayBrightnessStrategy = mock(DisplayBrightnessStrategy.class);
         int targetDisplayState = Display.STATE_DOZE;
@@ -70,6 +83,8 @@
                 targetDisplayState)).thenReturn(displayBrightnessStrategy);
         mDisplayBrightnessController.updateBrightness(displayPowerRequest, targetDisplayState);
         verify(displayBrightnessStrategy).updateBrightness(displayPowerRequest);
+        assertEquals(mDisplayBrightnessController.getCurrentDisplayBrightnessStrategyLocked(),
+                displayBrightnessStrategy);
     }
 
     @Test
@@ -77,4 +92,154 @@
         mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig();
         verify(mDisplayBrightnessStrategySelector).isAllowAutoBrightnessWhileDozingConfig();
     }
+
+    @Test
+    public void setTemporaryBrightness() {
+        float temporaryBrightness = 0.4f;
+        TemporaryBrightnessStrategy temporaryBrightnessStrategy = mock(
+                TemporaryBrightnessStrategy.class);
+        when(mDisplayBrightnessStrategySelector.getTemporaryDisplayBrightnessStrategy()).thenReturn(
+                temporaryBrightnessStrategy);
+        mDisplayBrightnessController.setTemporaryBrightness(temporaryBrightness);
+        verify(temporaryBrightnessStrategy).setTemporaryScreenBrightness(temporaryBrightness);
+    }
+
+    @Test
+    public void setCurrentScreenBrightness() {
+        // Current Screen brightness is set as expected when a different value than the current
+        // is set
+        float currentScreenBrightness = 0.4f;
+        mDisplayBrightnessController.setCurrentScreenBrightness(currentScreenBrightness);
+        assertEquals(mDisplayBrightnessController.getCurrentBrightness(),
+                currentScreenBrightness, 0.0f);
+        verify(mOnBrightnessChangeRunnable).run();
+
+        // No change to the current screen brightness is same as the existing one
+        mDisplayBrightnessController.setCurrentScreenBrightness(currentScreenBrightness);
+        verifyNoMoreInteractions(mOnBrightnessChangeRunnable);
+    }
+
+    @Test
+    public void setPendingScreenBrightnessSetting() {
+        float pendingScreenBrightness = 0.4f;
+        mDisplayBrightnessController.setPendingScreenBrightness(pendingScreenBrightness);
+        assertEquals(mDisplayBrightnessController.getPendingScreenBrightness(),
+                pendingScreenBrightness, 0.0f);
+    }
+
+    @Test
+    public void updateUserSetScreenBrightness() {
+        // No brightness is set if the pending brightness is invalid
+        mDisplayBrightnessController.setPendingScreenBrightness(Float.NaN);
+        assertFalse(mDisplayBrightnessController.updateUserSetScreenBrightness());
+
+        // user set brightness is not set if the current and the pending brightness are same.
+        float currentBrightness = 0.4f;
+        TemporaryBrightnessStrategy temporaryBrightnessStrategy = mock(
+                TemporaryBrightnessStrategy.class);
+        when(mDisplayBrightnessStrategySelector.getTemporaryDisplayBrightnessStrategy()).thenReturn(
+                temporaryBrightnessStrategy);
+        mDisplayBrightnessController.setCurrentScreenBrightness(currentBrightness);
+        mDisplayBrightnessController.setPendingScreenBrightness(currentBrightness);
+        mDisplayBrightnessController.setTemporaryBrightness(currentBrightness);
+        assertFalse(mDisplayBrightnessController.updateUserSetScreenBrightness());
+        verify(temporaryBrightnessStrategy).setTemporaryScreenBrightness(
+                PowerManager.BRIGHTNESS_INVALID_FLOAT);
+        assertEquals(mDisplayBrightnessController.getPendingScreenBrightness(),
+                PowerManager.BRIGHTNESS_INVALID_FLOAT, 0.0f);
+
+        // user set brightness is set as expected
+        currentBrightness = 0.4f;
+        float pendingScreenBrightness = 0.3f;
+        float temporaryScreenBrightness = 0.2f;
+        mDisplayBrightnessController.setCurrentScreenBrightness(currentBrightness);
+        mDisplayBrightnessController.setPendingScreenBrightness(pendingScreenBrightness);
+        mDisplayBrightnessController.setTemporaryBrightness(temporaryScreenBrightness);
+        assertTrue(mDisplayBrightnessController.updateUserSetScreenBrightness());
+        assertEquals(mDisplayBrightnessController.getCurrentBrightness(),
+                pendingScreenBrightness, 0.0f);
+        assertEquals(mDisplayBrightnessController.getLastUserSetScreenBrightness(),
+                pendingScreenBrightness, 0.0f);
+        verify(mOnBrightnessChangeRunnable, times(2)).run();
+        verify(temporaryBrightnessStrategy, times(2))
+                .setTemporaryScreenBrightness(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+        assertEquals(mDisplayBrightnessController.getPendingScreenBrightness(),
+                PowerManager.BRIGHTNESS_INVALID_FLOAT, 0.0f);
+    }
+
+    @Test
+    public void registerBrightnessSettingChangeListener() {
+        BrightnessSetting.BrightnessSettingListener brightnessSettingListener = mock(
+                BrightnessSetting.BrightnessSettingListener.class);
+        mDisplayBrightnessController.registerBrightnessSettingChangeListener(
+                brightnessSettingListener);
+        verify(mBrightnessSetting).registerListener(brightnessSettingListener);
+        assertEquals(mDisplayBrightnessController.getBrightnessSettingListenerLocked(),
+                brightnessSettingListener);
+    }
+
+    @Test
+    public void getScreenBrightnessSetting() {
+        // getScreenBrightnessSetting returns the value relayed by BrightnessSetting, if the
+        // valid is valid and in range
+        float brightnessSetting = 0.2f;
+        when(mBrightnessSetting.getBrightness()).thenReturn(brightnessSetting);
+        assertEquals(mDisplayBrightnessController.getScreenBrightnessSetting(), brightnessSetting,
+                0.0f);
+
+        // getScreenBrightnessSetting value is clamped if BrightnessSetting returns value beyond max
+        brightnessSetting = 1.1f;
+        when(mBrightnessSetting.getBrightness()).thenReturn(brightnessSetting);
+        assertEquals(mDisplayBrightnessController.getScreenBrightnessSetting(), 1.0f,
+                0.0f);
+
+        // getScreenBrightnessSetting returns default value is BrightnessSetting returns invalid
+        // value.
+        brightnessSetting = Float.NaN;
+        when(mBrightnessSetting.getBrightness()).thenReturn(brightnessSetting);
+        assertEquals(mDisplayBrightnessController.getScreenBrightnessSetting(), DEFAULT_BRIGHTNESS,
+                0.0f);
+    }
+
+    @Test
+    public void setBrightnessSetsInBrightnessSetting() {
+        float brightnessValue = 0.3f;
+        mDisplayBrightnessController.setBrightness(brightnessValue);
+        verify(mBrightnessSetting).setBrightness(brightnessValue);
+    }
+
+    @Test
+    public void updateScreenBrightnessSetting() {
+        // This interaction happens in the constructor itself
+        verify(mBrightnessSetting).getBrightness();
+
+        // Sets the appropriate value when valid, and not equal to the current brightness
+        float brightnessValue = 0.3f;
+        mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue);
+        assertEquals(mDisplayBrightnessController.getCurrentBrightness(), brightnessValue,
+                0.0f);
+        verify(mOnBrightnessChangeRunnable).run();
+        verify(mBrightnessSetting).setBrightness(brightnessValue);
+
+        // Does nothing if the value is invalid
+        mDisplayBrightnessController.updateScreenBrightnessSetting(Float.NaN);
+        verifyNoMoreInteractions(mOnBrightnessChangeRunnable, mBrightnessSetting);
+
+        // Does nothing if the value is same as the current brightness
+        brightnessValue = 0.2f;
+        mDisplayBrightnessController.setCurrentScreenBrightness(brightnessValue);
+        verify(mOnBrightnessChangeRunnable, times(2)).run();
+        mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue);
+        verifyNoMoreInteractions(mOnBrightnessChangeRunnable, mBrightnessSetting);
+    }
+
+    @Test
+    public void stop() {
+        BrightnessSetting.BrightnessSettingListener brightnessSettingListener = mock(
+                BrightnessSetting.BrightnessSettingListener.class);
+        mDisplayBrightnessController.registerBrightnessSettingChangeListener(
+                brightnessSettingListener);
+        mDisplayBrightnessController.stop();
+        verify(mBrightnessSetting).unregisterListener(brightnessSettingListener);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
index f2e03aa..e871fc5 100644
--- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
@@ -37,7 +37,6 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -165,18 +164,6 @@
                 awaitJobStart(DEFAULT_WAIT_TIMEOUT));
     }
 
-    @FlakyTest
-    @Test
-    public void testFeatureFlag() throws Exception {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.FORCED_APP_STANDBY_ENABLED, 0);
-        scheduleAndAssertJobStarted();
-        setAppOpsModeAllowed(false);
-        mIActivityManager.makePackageIdle(TEST_APP_PACKAGE, UserHandle.USER_CURRENT);
-        assertFalse("Job stopped even when feature flag was disabled",
-                awaitJobStop(DEFAULT_WAIT_TIMEOUT, JobParameters.STOP_REASON_UNDEFINED));
-    }
-
     @After
     public void tearDown() throws Exception {
         final Intent cancelJobsIntent = new Intent(TestJobActivity.ACTION_CANCEL_JOBS);
@@ -187,8 +174,6 @@
         Thread.sleep(500); // To avoid race with register in the next setUp
         setAppOpsModeAllowed(true);
         setPowerExemption(false);
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.FORCED_APP_STANDBY_ENABLED, 1);
     }
 
     private void setPowerExemption(boolean exempt) throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index 26d0ddf..ade1bd4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -64,6 +64,8 @@
                 .setUseParentsContacts(false)
                 .setCrossProfileIntentFilterAccessControl(10)
                 .setCrossProfileIntentResolutionStrategy(0)
+                .setMediaSharedWithParent(false)
+                .setCredentialShareableWithParent(true)
                 .build();
         final UserProperties actualProps = new UserProperties(defaultProps);
         actualProps.setShowInLauncher(14);
@@ -72,6 +74,8 @@
         actualProps.setUseParentsContacts(true);
         actualProps.setCrossProfileIntentFilterAccessControl(20);
         actualProps.setCrossProfileIntentResolutionStrategy(1);
+        actualProps.setMediaSharedWithParent(true);
+        actualProps.setCredentialShareableWithParent(false);
 
         // Write the properties to xml.
         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -111,6 +115,7 @@
                 .setStartWithParent(true)
                 .setShowInSettings(3452)
                 .setInheritDevicePolicy(1732)
+                .setMediaSharedWithParent(true)
                 .build();
         final UserProperties orig = new UserProperties(defaultProps);
         orig.setShowInLauncher(2841);
@@ -169,7 +174,10 @@
 
         // Items with no permission requirements.
         assertEqualGetterOrThrows(orig::getShowInLauncher, copy::getShowInLauncher, true);
-
+        assertEqualGetterOrThrows(orig::isMediaSharedWithParent,
+                copy::isMediaSharedWithParent, true);
+        assertEqualGetterOrThrows(orig::isCredentialShareableWithParent,
+                copy::isCredentialShareableWithParent, true);
     }
 
     /**
@@ -215,5 +223,9 @@
                 .isEqualTo(actual.getCrossProfileIntentFilterAccessControl());
         assertThat(expected.getCrossProfileIntentResolutionStrategy())
                 .isEqualTo(actual.getCrossProfileIntentResolutionStrategy());
+        assertThat(expected.isMediaSharedWithParent())
+                .isEqualTo(actual.isMediaSharedWithParent());
+        assertThat(expected.isCredentialShareableWithParent())
+                .isEqualTo(actual.isCredentialShareableWithParent());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 928c6ef..702059d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -86,7 +86,9 @@
                 .setShowInLauncher(17)
                 .setUseParentsContacts(true)
                 .setCrossProfileIntentFilterAccessControl(10)
-                .setCrossProfileIntentResolutionStrategy(1);
+                .setCrossProfileIntentResolutionStrategy(1)
+                .setMediaSharedWithParent(true)
+                .setCredentialShareableWithParent(false);
         final UserTypeDetails type = new UserTypeDetails.Builder()
                 .setName("a.name")
                 .setEnabled(1)
@@ -148,6 +150,8 @@
                 .getCrossProfileIntentFilterAccessControl());
         assertEquals(1, type.getDefaultUserPropertiesReference()
                 .getCrossProfileIntentResolutionStrategy());
+        assertTrue(type.getDefaultUserPropertiesReference().isMediaSharedWithParent());
+        assertFalse(type.getDefaultUserPropertiesReference().isCredentialShareableWithParent());
 
         assertEquals(23, type.getBadgeLabel(0));
         assertEquals(24, type.getBadgeLabel(1));
@@ -196,6 +200,8 @@
         assertEquals(UserProperties.SHOW_IN_LAUNCHER_WITH_PARENT, props.getShowInLauncher());
         assertEquals(UserProperties.CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT,
                 props.getCrossProfileIntentResolutionStrategy());
+        assertFalse(props.isMediaSharedWithParent());
+        assertFalse(props.isCredentialShareableWithParent());
 
         assertFalse(type.hasBadge());
     }
@@ -279,7 +285,9 @@
                 .setStartWithParent(true)
                 .setUseParentsContacts(true)
                 .setCrossProfileIntentFilterAccessControl(10)
-                .setCrossProfileIntentResolutionStrategy(1);
+                .setCrossProfileIntentResolutionStrategy(1)
+                .setMediaSharedWithParent(false)
+                .setCredentialShareableWithParent(true);
         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
         builders.put(userTypeAosp1, new UserTypeDetails.Builder()
                 .setName(userTypeAosp1)
@@ -312,6 +320,9 @@
         assertTrue(aospType.getDefaultUserPropertiesReference().getStartWithParent());
         assertTrue(aospType.getDefaultUserPropertiesReference()
                 .getUseParentsContacts());
+        assertFalse(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
+        assertTrue(aospType.getDefaultUserPropertiesReference()
+                .isCredentialShareableWithParent());
 
         // userTypeAosp2 should be modified.
         aospType = builders.get(userTypeAosp2).createUserTypeDetails();
@@ -348,6 +359,9 @@
         assertFalse(aospType.getDefaultUserPropertiesReference().getStartWithParent());
         assertFalse(aospType.getDefaultUserPropertiesReference()
                 .getUseParentsContacts());
+        assertTrue(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
+        assertFalse(aospType.getDefaultUserPropertiesReference()
+                .isCredentialShareableWithParent());
 
         // userTypeOem1 should be created.
         UserTypeDetails.Builder customType = builders.get(userTypeOem1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 1305e07..ac5bcff 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -19,7 +19,6 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 import static org.testng.Assert.assertThrows;
@@ -189,6 +188,10 @@
                 cloneUserProperties::getCrossProfileIntentFilterAccessControl);
         assertThrows(SecurityException.class,
                 cloneUserProperties::getCrossProfileIntentResolutionStrategy);
+        assertThat(typeProps.isMediaSharedWithParent())
+                .isEqualTo(cloneUserProperties.isMediaSharedWithParent());
+        assertThat(typeProps.isCredentialShareableWithParent())
+                .isEqualTo(cloneUserProperties.isCredentialShareableWithParent());
 
         // Verify clone user parent
         assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
@@ -834,11 +837,13 @@
         // provided that the test caller has the necessary permissions.
         assertThat(userProps.getShowInLauncher()).isEqualTo(typeProps.getShowInLauncher());
         assertThat(userProps.getShowInSettings()).isEqualTo(typeProps.getShowInSettings());
-        assertFalse(userProps.getUseParentsContacts());
+        assertThat(userProps.getUseParentsContacts()).isFalse();
         assertThrows(SecurityException.class, userProps::getCrossProfileIntentFilterAccessControl);
         assertThrows(SecurityException.class, userProps::getCrossProfileIntentResolutionStrategy);
         assertThrows(SecurityException.class, userProps::getStartWithParent);
         assertThrows(SecurityException.class, userProps::getInheritDevicePolicy);
+        assertThat(userProps.isMediaSharedWithParent()).isFalse();
+        assertThat(userProps.isCredentialShareableWithParent()).isTrue();
     }
 
     // Make sure only max managed profiles can be created
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
new file mode 100644
index 0000000..0be678a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.rollback;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.pm.VersionedPackage;
+import android.util.Log;
+import android.util.Xml;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.SystemConfig;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Scanner;
+
+@RunWith(AndroidJUnit4.class)
+public class RollbackPackageHealthObserverTest {
+    private static final String LOG_TAG = "RollbackPackageHealthObserverTest";
+
+    private SystemConfig mSysConfig;
+
+    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+    @Before
+    public void setup() {
+        mSysConfig = new SystemConfigTestClass();
+    }
+
+    /**
+    * Subclass of SystemConfig without running the constructor.
+    */
+    private class SystemConfigTestClass extends SystemConfig {
+        SystemConfigTestClass() {
+          super(false);
+        }
+    }
+
+    /**
+     * Test that isAutomaticRollbackDenied works correctly when packages that are not
+     * denied are sent.
+     */
+    @Test
+    public void isRollbackAllowedTest_false() throws IOException {
+        final String contents =
+                "<config>\n"
+                + "    <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n"
+                + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
+
+        readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig,
+            new VersionedPackage("com.test.package", 1))).isEqualTo(false);
+    }
+
+    /**
+     * Test that isAutomaticRollbackDenied works correctly when packages that are
+     * denied are sent.
+     */
+    @Test
+    public void isRollbackAllowedTest_true() throws IOException {
+        final String contents =
+                "<config>\n"
+                + "    <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n"
+                + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
+
+        readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig,
+            new VersionedPackage("com.android.vending", 1))).isEqualTo(true);
+    }
+
+    /**
+     * Test that isAutomaticRollbackDenied works correctly when no config is present
+     */
+    @Test
+    public void isRollbackAllowedTest_noConfig() throws IOException {
+        final File folder = createTempSubfolder("folder");
+
+        readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig,
+            new VersionedPackage("com.android.vending", 1))).isEqualTo(false);
+    }
+
+    /**
+     * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
+     *
+     * @param folder   pre-existing subdirectory of mTemporaryFolder to put the file
+     * @param fileName name of the file (e.g. filename.xml) to create
+     * @param contents contents to write to the file
+     * @return the newly created file
+     */
+    private File createTempFile(File folder, String fileName, String contents)
+            throws IOException {
+        File file = new File(folder, fileName);
+        BufferedWriter bw = new BufferedWriter(new FileWriter(file));
+        bw.write(contents);
+        bw.close();
+
+        // Print to logcat for test debugging.
+        Log.d(LOG_TAG, "Contents of file " + file.getAbsolutePath());
+        Scanner input = new Scanner(file);
+        while (input.hasNextLine()) {
+            Log.d(LOG_TAG, input.nextLine());
+        }
+
+        return file;
+    }
+
+    private void readPermissions(File libraryDir, int permissionFlag) {
+        final XmlPullParser parser = Xml.newPullParser();
+        mSysConfig.readPermissions(parser, libraryDir, permissionFlag);
+    }
+
+    /**
+     * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
+     *
+     * @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed
+     * @return the folder
+     */
+    private File createTempSubfolder(String folderName)
+            throws IOException {
+        File folder = new File(mTemporaryFolder.getRoot(), folderName);
+        folder.mkdirs();
+        return folder;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index d073f5b..aca96ad 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -595,6 +595,56 @@
     }
 
     /**
+     * Test that getRollbackDenylistedPackages works correctly for the tag:
+     * {@code automatic-rollback-denylisted-app}.
+     */
+    @Test
+    public void automaticRollbackDeny_vending() throws IOException {
+        final String contents =
+                "<config>\n"
+                + "    <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n"
+                + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
+
+        readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages())
+            .containsExactly("com.android.vending");
+    }
+
+    /**
+     * Test that getRollbackDenylistedPackages works correctly for the tag:
+     * {@code automatic-rollback-denylisted-app} without any packages.
+     */
+    @Test
+    public void automaticRollbackDeny_empty() throws IOException {
+        final String contents =
+                "<config>\n"
+                + "    <automatic-rollback-denylisted-app />\n"
+                + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
+
+        readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()).isEmpty();
+    }
+
+    /**
+     * Test that getRollbackDenylistedPackages works correctly for the tag:
+     * {@code automatic-rollback-denylisted-app} without the corresponding config.
+     */
+    @Test
+    public void automaticRollbackDeny_noConfig() throws IOException {
+        final File folder = createTempSubfolder("folder");
+
+        readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()).isEmpty();
+    }
+
+    /**
      * Tests that readPermissions works correctly for the tag: {@code update-ownership}.
      */
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index dcf1b35..9570ff6 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -87,6 +87,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.hardware.display.DisplayManager;
+import android.os.BatteryStats;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -119,6 +120,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -249,6 +251,8 @@
                         .setLong("elapsed_threshold_rare", RARE_THRESHOLD)
                         .setLong("elapsed_threshold_restricted", RESTRICTED_THRESHOLD);
         DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener;
+        String mExpectedNoteEventPackage = null;
+        int mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE;
 
         MyInjector(Context context, Looper looper) {
             super(context, looper);
@@ -320,6 +324,9 @@
 
         @Override
         void noteEvent(int event, String packageName, int uid) throws RemoteException {
+            if (Objects.equals(mExpectedNoteEventPackage, packageName)) {
+                mLastNoteEvent = event;
+            }
         }
 
         @Override
@@ -2103,6 +2110,50 @@
         assertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_BACKGROUND_LOCATION);
     }
 
+    @Test
+    public void testBatteryStatsNoteEvent() throws Exception {
+        mInjector.mExpectedNoteEventPackage = PACKAGE_1;
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+                REASON_MAIN_FORCED_BY_USER);
+        assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent);
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+                REASON_MAIN_FORCED_BY_USER);
+        assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent);
+
+        // Since we're staying on the PACKAGE_ACTIVE side, noteEvent shouldn't be called.
+        // Reset the last event to confirm the method isn't called.
+        mInjector.mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_FORCED_BY_USER);
+        assertEquals(BatteryStats.HistoryItem.EVENT_NONE, mInjector.mLastNoteEvent);
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+                REASON_MAIN_FORCED_BY_USER);
+        assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent);
+
+        // Since we're staying on the PACKAGE_ACTIVE side, noteEvent shouldn't be called.
+        // Reset the last event to confirm the method isn't called.
+        mInjector.mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_USER);
+        assertEquals(BatteryStats.HistoryItem.EVENT_NONE, mInjector.mLastNoteEvent);
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+                REASON_MAIN_FORCED_BY_USER);
+        assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent);
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_USER);
+        assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent);
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_EXEMPTED,
+                REASON_MAIN_FORCED_BY_USER);
+        assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent);
+    }
+
     private String getAdminAppsStr(int userId) {
         return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
     }
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index fd1ca68..a76b82b 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -407,7 +407,7 @@
 
     void assertShowRecentApps() {
         waitForIdle();
-        verify(mStatusBarManagerInternal).showRecentApps(anyBoolean(), anyBoolean());
+        verify(mStatusBarManagerInternal).showRecentApps(anyBoolean());
     }
 
     void assertSwitchKeyboardLayout() {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java
index 3163820..20f03d8 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.usage;
 
+import android.annotation.UserIdInt;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
@@ -35,65 +36,71 @@
 public class UsageStatsIdleService extends JobService {
 
     /**
-     * Base job ID for the pruning job - must be unique within the system server uid.
+     * Namespace for prune job
      */
-    private static final int PRUNE_JOB_ID = 546357475;
+    private static final String PRUNE_JOB_NS = "usagestats_prune";
+
     /**
-     * Job ID for the update mappings job - must be unique within the system server uid.
-     * Incrementing PRUNE_JOB_ID by 21475 (MAX_USER_ID) to ensure there is no overlap in job ids.
+     * Namespace for update mappings job
      */
-    private static final int UPDATE_MAPPINGS_JOB_ID = 546378950;
+    private static final String UPDATE_MAPPINGS_JOB_NS = "usagestats_mapping";
 
     private static final String USER_ID_KEY = "user_id";
 
-    static void scheduleJob(Context context, int userId) {
-        final int userJobId = PRUNE_JOB_ID + userId; // unique job id per user
+    /** Schedule a prune job */
+    static void schedulePruneJob(Context context, @UserIdInt int userId) {
         final ComponentName component = new ComponentName(context.getPackageName(),
                 UsageStatsIdleService.class.getName());
         final PersistableBundle bundle = new PersistableBundle();
         bundle.putInt(USER_ID_KEY, userId);
-        final JobInfo pruneJob = new JobInfo.Builder(userJobId, component)
+        final JobInfo pruneJob = new JobInfo.Builder(userId, component)
                 .setRequiresDeviceIdle(true)
                 .setExtras(bundle)
                 .setPersisted(true)
                 .build();
 
-        scheduleJobInternal(context, pruneJob, userJobId);
+        scheduleJobInternal(context, pruneJob, PRUNE_JOB_NS, userId);
     }
 
-    static void scheduleUpdateMappingsJob(Context context) {
+    static void scheduleUpdateMappingsJob(Context context, @UserIdInt int userId) {
         final ComponentName component = new ComponentName(context.getPackageName(),
                 UsageStatsIdleService.class.getName());
-        final JobInfo updateMappingsJob = new JobInfo.Builder(UPDATE_MAPPINGS_JOB_ID, component)
+        final PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(USER_ID_KEY, userId);
+        final JobInfo updateMappingsJob = new JobInfo.Builder(userId, component)
                 .setPersisted(true)
                 .setMinimumLatency(TimeUnit.DAYS.toMillis(1))
                 .setOverrideDeadline(TimeUnit.DAYS.toMillis(2))
+                .setExtras(bundle)
                 .build();
 
-        scheduleJobInternal(context, updateMappingsJob, UPDATE_MAPPINGS_JOB_ID);
+        scheduleJobInternal(context, updateMappingsJob, UPDATE_MAPPINGS_JOB_NS, userId);
     }
 
-    private static void scheduleJobInternal(Context context, JobInfo pruneJob, int jobId) {
-        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
-        final JobInfo pendingPruneJob = jobScheduler.getPendingJob(jobId);
-        // only schedule a new prune job if one doesn't exist already for this user
-        if (!pruneJob.equals(pendingPruneJob)) {
-            jobScheduler.cancel(jobId); // cancel any previously scheduled prune job
-            jobScheduler.schedule(pruneJob);
+    private static void scheduleJobInternal(Context context, JobInfo jobInfo,
+            String namespace, int jobId) {
+        JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+        jobScheduler = jobScheduler.forNamespace(namespace);
+        final JobInfo pendingJob = jobScheduler.getPendingJob(jobId);
+        // only schedule a new job if one doesn't exist already for this user
+        if (!jobInfo.equals(pendingJob)) {
+            jobScheduler.cancel(jobId); // cancel any previously scheduled job
+            jobScheduler.schedule(jobInfo);
         }
     }
 
-    static void cancelJob(Context context, int userId) {
-        cancelJobInternal(context, PRUNE_JOB_ID + userId);
+    static void cancelPruneJob(Context context, @UserIdInt int userId) {
+        cancelJobInternal(context, PRUNE_JOB_NS, userId);
     }
 
-    static void cancelUpdateMappingsJob(Context context) {
-        cancelJobInternal(context, UPDATE_MAPPINGS_JOB_ID);
+    static void cancelUpdateMappingsJob(Context context, @UserIdInt int userId) {
+        cancelJobInternal(context, UPDATE_MAPPINGS_JOB_NS, userId);
     }
 
-    private static void cancelJobInternal(Context context, int jobId) {
-        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+    private static void cancelJobInternal(Context context, String namespace, int jobId) {
+        JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
         if (jobScheduler != null) {
+            jobScheduler = jobScheduler.forNamespace(namespace);
             jobScheduler.cancel(jobId);
         }
     }
@@ -102,15 +109,19 @@
     public boolean onStartJob(JobParameters params) {
         final PersistableBundle bundle = params.getExtras();
         final int userId = bundle.getInt(USER_ID_KEY, -1);
-        if (userId == -1 && params.getJobId() != UPDATE_MAPPINGS_JOB_ID) {
+
+        if (userId == -1) { // legacy job
             return false;
         }
 
+        // Do async
         AsyncTask.execute(() -> {
             final UsageStatsManagerInternal usageStatsManagerInternal = LocalServices.getService(
                     UsageStatsManagerInternal.class);
-            if (params.getJobId() == UPDATE_MAPPINGS_JOB_ID) {
-                final boolean jobFinished = usageStatsManagerInternal.updatePackageMappingsData();
+            final String jobNs = params.getJobNamespace();
+            if (UPDATE_MAPPINGS_JOB_NS.equals(jobNs)) {
+                final boolean jobFinished =
+                        usageStatsManagerInternal.updatePackageMappingsData(userId);
                 jobFinished(params, !jobFinished); // reschedule if data was not updated
             } else {
                 final boolean jobFinished =
@@ -118,6 +129,8 @@
                 jobFinished(params, !jobFinished); // reschedule if data was not pruned
             }
         });
+
+        // Job is running asynchronously
         return true;
     }
 
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index b3a1f2b..7ff5b4a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -433,11 +433,9 @@
     private void onUserUnlocked(int userId) {
         // fetch the installed packages outside the lock so it doesn't block package manager.
         final HashMap<String, Long> installedPackages = getInstalledPackages(userId);
-        // delay updating of package mappings for user 0 since their data is not likely to be stale.
-        // this also makes it less likely for restored data to be erased on unexpected reboots.
-        if (userId == UserHandle.USER_SYSTEM) {
-            UsageStatsIdleService.scheduleUpdateMappingsJob(getContext());
-        }
+
+        UsageStatsIdleService.scheduleUpdateMappingsJob(getContext(), userId);
+
         final boolean deleteObsoleteData = shouldDeleteObsoleteData(UserHandle.of(userId));
         synchronized (mLock) {
             // This should be safe to add this early. Other than reportEventOrAddToQueue and
@@ -1261,8 +1259,8 @@
         }
         mAppStandby.onUserRemoved(userId);
         // Cancel any scheduled jobs for this user since the user is being removed.
-        UsageStatsIdleService.cancelJob(getContext(), userId);
-        UsageStatsIdleService.cancelUpdateMappingsJob(getContext());
+        UsageStatsIdleService.cancelPruneJob(getContext(), userId);
+        UsageStatsIdleService.cancelUpdateMappingsJob(getContext(), userId);
     }
 
     /**
@@ -1300,7 +1298,7 @@
 
         // Schedule a job to prune any data related to this package.
         if (tokenRemoved != PackagesTokenData.UNASSIGNED_TOKEN) {
-            UsageStatsIdleService.scheduleJob(getContext(), userId);
+            UsageStatsIdleService.schedulePruneJob(getContext(), userId);
         }
     }
 
@@ -1325,19 +1323,19 @@
     /**
      * Called by the Binder stub.
      */
-    private boolean updatePackageMappingsData() {
+    private boolean updatePackageMappingsData(@UserIdInt int userId) {
         // don't update the mappings if a profile user is defined
-        if (!shouldDeleteObsoleteData(UserHandle.SYSTEM)) {
+        if (!shouldDeleteObsoleteData(UserHandle.of(userId))) {
             return true; // return true so job scheduler doesn't reschedule the job
         }
         // fetch the installed packages outside the lock so it doesn't block package manager.
-        final HashMap<String, Long> installedPkgs = getInstalledPackages(UserHandle.USER_SYSTEM);
+        final HashMap<String, Long> installedPkgs = getInstalledPackages(userId);
         synchronized (mLock) {
-            if (!mUserUnlockedStates.contains(UserHandle.USER_SYSTEM)) {
+            if (!mUserUnlockedStates.contains(userId)) {
                 return false; // user is no longer unlocked
             }
 
-            final UserUsageStatsService userService = mUserState.get(UserHandle.USER_SYSTEM);
+            final UserUsageStatsService userService = mUserState.get(userId);
             if (userService == null) {
                 return false; // user was stopped or removed
             }
@@ -3055,44 +3053,35 @@
         }
 
         @Override
-        public byte[] getBackupPayload(int user, String key) {
-            if (!mUserUnlockedStates.contains(user)) {
-                Slog.w(TAG, "Failed to get backup payload for locked user " + user);
+        public byte[] getBackupPayload(@UserIdInt int userId, String key) {
+            if (!mUserUnlockedStates.contains(userId)) {
+                Slog.w(TAG, "Failed to get backup payload for locked user " + userId);
                 return null;
             }
             synchronized (mLock) {
-                // Check to ensure that only user 0's data is b/r for now
-                // Note: if backup and restore is enabled for users other than the system user, the
-                // #onUserUnlocked logic, specifically when the update mappings job is scheduled via
-                // UsageStatsIdleService.scheduleUpdateMappingsJob, will have to be updated.
-                if (user == UserHandle.USER_SYSTEM) {
-                    final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user);
-                    if (userStats == null) {
-                        return null; // user was stopped or removed
-                    }
-                    return userStats.getBackupPayload(key);
-                } else {
-                    return null;
+                final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(userId);
+                if (userStats == null) {
+                    return null; // user was stopped or removed
                 }
+                Slog.i(TAG, "Returning backup payload for u=" + userId);
+                return userStats.getBackupPayload(key);
             }
         }
 
         @Override
-        public void applyRestoredPayload(int user, String key, byte[] payload) {
+        public void applyRestoredPayload(@UserIdInt int userId, String key, byte[] payload) {
             synchronized (mLock) {
-                if (!mUserUnlockedStates.contains(user)) {
-                    Slog.w(TAG, "Failed to apply restored payload for locked user " + user);
+                if (!mUserUnlockedStates.contains(userId)) {
+                    Slog.w(TAG, "Failed to apply restored payload for locked user " + userId);
                     return;
                 }
 
-                if (user == UserHandle.USER_SYSTEM) {
-                    final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user);
-                    if (userStats == null) {
-                        return; // user was stopped or removed
-                    }
-                    final Set<String> restoredApps = userStats.applyRestoredPayload(key, payload);
-                    mAppStandby.restoreAppsToRare(restoredApps, user);
+                final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(userId);
+                if (userStats == null) {
+                    return; // user was stopped or removed
                 }
+                final Set<String> restoredApps = userStats.applyRestoredPayload(key, payload);
+                mAppStandby.restoreAppsToRare(restoredApps, userId);
             }
         }
 
@@ -3165,8 +3154,8 @@
         }
 
         @Override
-        public boolean updatePackageMappingsData() {
-            return UsageStatsService.this.updatePackageMappingsData();
+        public boolean updatePackageMappingsData(@UserIdInt int userId) {
+            return UsageStatsService.this.updatePackageMappingsData(userId);
         }
 
         /**
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index fd2907c..95a8e16 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -32,10 +32,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
 
 /**
  * A unified virtual device providing a means of voice (and other) communication on a device.
@@ -150,14 +148,6 @@
 
     private final Object mLock = new Object();
 
-    // Future used to delay terminating the InCallService before the call disconnect tone
-    // finishes playing.
-    private static Map<String, CompletableFuture<Void>> sDisconnectedToneFutures = new ArrayMap<>();
-
-    // Timeout value to be used to ensure future completion for sDisconnectedToneFutures. This is
-    // set to 4 seconds to account for the exceptional case (TONE_CONGESTION).
-    private static final int DISCONNECTED_TONE_TIMEOUT = 4000;
-
     Phone(InCallAdapter adapter, String callingPackage, int targetSdkVersion) {
         mInCallAdapter = adapter;
         mCallingPackage = callingPackage;
@@ -466,45 +456,9 @@
     }
 
     private void fireCallRemoved(Call call) {
-        String callId = call.internalGetCallId();
-        CompletableFuture<Void> disconnectedToneFuture = initializeDisconnectedToneFuture(callId);
-        // delay the InCallService termination until after the disconnect tone finishes playing
-        disconnectedToneFuture.thenRunAsync(() -> {
-            for (Listener listener : mListeners) {
-                listener.onCallRemoved(this, call);
-            }
-            // clean up the future after
-            sDisconnectedToneFutures.remove(callId);
-        });
-    }
-
-    /**
-     * Initialize disconnect tone future to be used in delaying ICS termination.
-     *
-     * @return CompletableFuture to delay InCallService termination until after the disconnect tone
-     * finishes playing. A timeout of 4s is used to handle the use case when we play
-     * TONE_CONGESTION and to ensure completion so that we don't block the removal of the service.
-     */
-    private CompletableFuture<Void> initializeDisconnectedToneFuture(String callId) {
-        // create the future and map (sDisconnectedToneFutures) it to the corresponding call id
-        CompletableFuture<Void> disconnectedToneFuture = new CompletableFuture<Void>()
-                .completeOnTimeout(null, DISCONNECTED_TONE_TIMEOUT, TimeUnit.MILLISECONDS);
-        // we should not encounter duplicate insertions since call ids are unique
-        sDisconnectedToneFutures.put(callId, disconnectedToneFuture);
-        return disconnectedToneFuture;
-    }
-
-    /**
-     * Completes disconnected tone future with passed in result.
-     * @hide
-     * @return true if future was completed, false otherwise
-     */
-    public static boolean completeDisconnectedToneFuture(String callId) {
-        if (sDisconnectedToneFutures.containsKey(callId)) {
-            sDisconnectedToneFutures.get(callId).complete(null);
-            return true;
+        for (Listener listener : mListeners) {
+            listener.onCallRemoved(this, call);
         }
-        return false;
     }
 
     private void fireCallAudioStateChanged(CallAudioState audioState) {
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 26b4bbc..40488b1 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2547,8 +2547,6 @@
 
     /**
      * User is not associated with the subscription.
-     * TODO(b/263279115): Make this error code public.
-     * @hide
      */
     public static final int RESULT_USER_NOT_ALLOWED = 33;
 
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index a3b7260..189a08c 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2680,10 +2680,10 @@
      * @param columnName Column name in subscription database.
      *
      * @return Value in string format associated with {@code subscriptionId} and {@code columnName}
-     * from the database.
+     * from the database. Empty string if the {@code subscriptionId} is invalid (for backward
+     * compatible).
      *
-     * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
-     * exposed.
+     * @throws IllegalArgumentException if the field is not exposed.
      *
      * @see android.provider.Telephony.SimInfo for all the columns.
      *
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 83b9098..37d1e94 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3242,17 +3242,17 @@
             case NETWORK_TYPE_CDMA:
                 return "CDMA";
             case NETWORK_TYPE_EVDO_0:
-                return "CDMA - EvDo rev. 0";
+                return "EVDO-0";
             case NETWORK_TYPE_EVDO_A:
-                return "CDMA - EvDo rev. A";
+                return "EVDO-A";
             case NETWORK_TYPE_EVDO_B:
-                return "CDMA - EvDo rev. B";
+                return "EVDO-B";
             case NETWORK_TYPE_1xRTT:
-                return "CDMA - 1xRTT";
+                return "1xRTT";
             case NETWORK_TYPE_LTE:
                 return "LTE";
             case NETWORK_TYPE_EHRPD:
-                return "CDMA - eHRPD";
+                return "eHRPD";
             case NETWORK_TYPE_IDEN:
                 return "iDEN";
             case NETWORK_TYPE_HSPAP:
@@ -3260,7 +3260,7 @@
             case NETWORK_TYPE_GSM:
                 return "GSM";
             case NETWORK_TYPE_TD_SCDMA:
-                return "TD_SCDMA";
+                return "TD-SCDMA";
             case NETWORK_TYPE_IWLAN:
                 return "IWLAN";
             case NETWORK_TYPE_LTE_CA:
@@ -9574,10 +9574,10 @@
      */
     public static boolean isValidAllowedNetworkTypesReason(@AllowedNetworkTypesReason int reason) {
         switch (reason) {
-            case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER:
-            case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER:
-            case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER:
-            case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
+            case ALLOWED_NETWORK_TYPES_REASON_USER:
+            case ALLOWED_NETWORK_TYPES_REASON_POWER:
+            case ALLOWED_NETWORK_TYPES_REASON_CARRIER:
+            case ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
             case ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS:
                 return true;
         }
@@ -18118,4 +18118,44 @@
                 return "UNKNOWN(" + state + ")";
         }
     }
+
+    /**
+     * Convert the allowed network types reason to string.
+     *
+     * @param reason The allowed network types reason.
+     * @return The converted string.
+     *
+     * @hide
+     */
+    @NonNull
+    public static String allowedNetworkTypesReasonToString(@AllowedNetworkTypesReason int reason) {
+        switch (reason) {
+            case ALLOWED_NETWORK_TYPES_REASON_USER: return "user";
+            case ALLOWED_NETWORK_TYPES_REASON_POWER: return "power";
+            case ALLOWED_NETWORK_TYPES_REASON_CARRIER: return "carrier";
+            case ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G: return "enable_2g";
+            case ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS: return "user_restrictions";
+            default: return "unknown(" + reason + ")";
+        }
+    }
+
+    /**
+     * Convert the allowed network types reason from string.
+     *
+     * @param reason The reason in string format.
+     * @return The allowed network types reason.
+     *
+     * @hide
+     */
+    @AllowedNetworkTypesReason
+    public static int allowedNetworkTypesReasonFromString(@NonNull String reason) {
+        switch (reason) {
+            case "user": return ALLOWED_NETWORK_TYPES_REASON_USER;
+            case "power": return ALLOWED_NETWORK_TYPES_REASON_POWER;
+            case "carrier": return ALLOWED_NETWORK_TYPES_REASON_CARRIER;
+            case "enable_2g": return ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G;
+            case "user_restrictions": return ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS;
+            default: return -1;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index a2d2019..cdb7d7c 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -1570,8 +1570,8 @@
 
     /**
      * Returns whether the passing portIndex is available.
-     * A port is available if it is active without enabled profile on it or
-     * calling app has carrier privilege over the profile installed on the selected port.
+     * A port is available if it is active without an enabled profile on it or calling app can
+     * activate a new profile on the selected port without any user interaction.
      * Always returns false if the cardId is a physical card.
      *
      * @param portIndex is an enumeration of the ports available on the UICC.
diff --git a/tests/VectorDrawableTest/Android.bp b/tests/VectorDrawableTest/Android.bp
index 099d874..9da7c5f 100644
--- a/tests/VectorDrawableTest/Android.bp
+++ b/tests/VectorDrawableTest/Android.bp
@@ -26,7 +26,5 @@
 android_test {
     name: "VectorDrawableTest",
     srcs: ["**/*.java"],
-    // certificate set as platform to allow testing of @hidden APIs
-    certificate: "platform",
     platform_apis: true,
 }
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index 163e438..5334dac 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -158,15 +158,6 @@
                 <category android:name="com.android.test.dynamic.TEST"/>
             </intent-filter>
         </activity>
-        <activity android:name="LottieDrawableTest"
-             android:label="Lottie test bed"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-
-                <category android:name="com.android.test.dynamic.TEST" />
-            </intent-filter>
-        </activity>
     </application>
 
 </manifest>
diff --git a/tests/VectorDrawableTest/res/raw/lottie.json b/tests/VectorDrawableTest/res/raw/lottie.json
deleted file mode 100644
index fea571c..0000000
--- a/tests/VectorDrawableTest/res/raw/lottie.json
+++ /dev/null
@@ -1,123 +0,0 @@
-{
-    "v":"4.6.9",
-    "fr":60,
-    "ip":0,
-    "op":200,
-    "w":800,
-    "h":600,
-    "nm":"Loader 1 JSON",
-    "ddd":0,
-
-
-    "layers":[
-       {
-          "ddd":0,
-          "ind":1,
-          "ty":4,
-          "nm":"Custom Path 1",
-          "ao": 0,
-          "ip": 0,
-          "op": 300,
-          "st": 0,
-          "sr": 1,
-          "bm": 0,
-          "ks": {
-             "o": { "a":0, "k":100 },
-             "r": { "a":1, "k": [
-               { "s": [ 0 ], "e": [ 360], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 0 },
-           { "t": 200 }
-         ] },
-             "p": { "a":0, "k":[ 300, 300, 0 ] },
-             "a": { "a":0, "k":[ 100, 100, 0 ] },
-             "s": { "a":1, "k":[
-               { "s": [ 100, 100 ], "e": [ 200, 200 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 0 },
-               { "s": [ 200, 200 ], "e": [ 100, 100 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 100 },
-           { "t": 200 }
-             ] }
-          },
-
-          "shapes":[
-            {
-              "ty":"gr",
-              "it":[
-                {
-                  "ty" : "sh",
-                  "nm" : "Path 1",
-                  "ks" : {
-                    "a" : 1,
-                    "k" : [
-                      {
-                        "s": [ {
-                          "i": [ [   0,  50 ], [ -50,   0 ], [   0, -50 ], [  50,   0 ] ],
-                          "o": [ [   0, -50 ], [  50,   0 ], [   0,  50 ], [ -50,   0 ] ],
-                          "v": [ [   0, 100 ], [ 100,   0 ], [ 200, 100 ], [ 100, 200 ] ],
-                          "c": true
-                        } ],
-                        "e": [ {
-                          "i": [ [  50,  50 ], [ -50,   0 ], [ -50, -50 ], [  50,  50 ] ],
-                          "o": [ [  50, -50 ], [  50,   0 ], [ -50,  50 ], [ -50,  50 ] ],
-                          "v": [ [   0, 100 ], [ 100,   0 ], [ 200, 100 ], [ 100, 200 ] ],
-                          "c": true
-                        } ],
-                        "i": { "x":0.5, "y":0.5 },
-                        "o": { "x":0.5, "y":0.5 },
-                        "t": 0
-                      },
-                      {
-                        "s": [ {
-                          "i": [ [  50,  50 ], [ -50,   0 ], [ -50, -50 ], [  50,  50 ] ],
-                          "o": [ [  50, -50 ], [  50,   0 ], [ -50,  50 ], [ -50,  50 ] ],
-                          "v": [ [   0, 100 ], [ 100,   0 ], [ 200, 100 ], [ 100, 200 ] ],
-                          "c": true
-                        } ],
-                        "e": [ {
-                          "i": [ [   0,  50 ], [ -50,   0 ], [   0, -50 ], [  50,   0 ] ],
-                          "o": [ [   0, -50 ], [  50,   0 ], [   0,  50 ], [ -50,   0 ] ],
-                          "v": [ [   0, 100 ], [ 100,   0 ], [ 200, 100 ], [ 100, 200 ] ],
-                          "c": true
-                        } ],
-                        "i": { "x":0.5, "y":0.5 },
-                        "o": { "x":0.5, "y":0.5 },
-                        "t": 100
-                      },
-                      {
-                        "t": 200
-                      }
-                    ]
-                  }
-                },
-
-                {
-                  "ty": "st",
-                  "nm": "Stroke 1",
-                  "lc": 1,
-                  "lj": 1,
-                  "ml": 4,
-                  "w" : { "a": 1, "k": [
-                    { "s": [ 30 ], "e": [ 50 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 0 },
-                    { "s": [ 50 ], "e": [ 30 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 100 },
-            { "t": 200 }
-                  ] },
-                  "o" : { "a": 0, "k": 100 },
-                  "c" : { "a": 1, "k": [
-                    { "s": [ 0, 1, 0 ], "e": [ 1, 0, 0 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 0   },
-                    { "s": [ 1, 0, 0 ], "e": [ 0, 1, 0 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 100 },
-                    { "t": 200 }
-                  ] }
-                },
-
-                {
-                  "ty":"tr",
-                  "p" : { "a":0, "k":[   0,   0 ] },
-                  "a" : { "a":0, "k":[   0,   0 ] },
-                  "s" : { "a":0, "k":[ 100, 100 ] },
-                  "r" : { "a":0, "k":  0 },
-                  "o" : { "a":0, "k":100 },
-                  "nm": "Transform"
-                }
-              ]
-            }
-          ]
-       }
-    ]
- }
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/LottieDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/LottieDrawableTest.java
deleted file mode 100644
index 05eae7b..0000000
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/LottieDrawableTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.test.dynamic;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.LottieDrawable;
-import android.os.Bundle;
-import android.view.View;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Scanner;
-
-@SuppressWarnings({"UnusedDeclaration"})
-public class LottieDrawableTest extends Activity {
-    private static final String TAG = "LottieDrawableTest";
-    static final int BACKGROUND = 0xFFF44336;
-
-    class LottieDrawableView extends View {
-        private Rect mLottieBounds;
-
-        private LottieDrawable mLottie;
-
-        LottieDrawableView(Context context, InputStream is) {
-            super(context);
-            Scanner s = new Scanner(is).useDelimiter("\\A");
-            String json = s.hasNext() ? s.next() : "";
-            try {
-                mLottie = LottieDrawable.makeLottieDrawable(json);
-            } catch (IOException e) {
-                throw new RuntimeException(TAG + ": error parsing test Lottie");
-            }
-            mLottie.start();
-        }
-
-        @Override
-        protected void onDraw(Canvas canvas) {
-            canvas.drawColor(BACKGROUND);
-
-            mLottie.setBounds(mLottieBounds);
-            mLottie.draw(canvas);
-        }
-
-        public void setLottieSize(Rect bounds) {
-            mLottieBounds = bounds;
-        }
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        InputStream is = getResources().openRawResource(R.raw.lottie);
-
-        LottieDrawableView view = new LottieDrawableView(this, is);
-        view.setLottieSize(new Rect(0, 0, 900, 900));
-        setContentView(view);
-    }
-}
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 965b073..34f884b 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -19,9 +19,6 @@
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.vcn.VcnManager.VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY;
-import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
-import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
-import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
 import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
@@ -143,6 +140,8 @@
     @NonNull private TelephonySubscriptionTrackerCallback mCallback;
     @NonNull private TelephonySubscriptionTracker mTelephonySubscriptionTracker;
 
+    @NonNull private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
+
     public TelephonySubscriptionTrackerTest() {
         mContext = mock(Context.class);
         mTestLooper = new TestLooper();
@@ -173,7 +172,7 @@
                 .getSystemService(Context.CARRIER_CONFIG_SERVICE);
         doReturn(TEST_CARRIER_CONFIG)
                 .when(mCarrierConfigManager)
-                .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1));
+                .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1), any());
 
         // subId 1, 2 are in same subGrp, only subId 1 is active
         doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_1).getGroupUuid();
@@ -189,9 +188,15 @@
         doReturn(2).when(mTelephonyManager).getActiveModemCount();
 
         mCallback = mock(TelephonySubscriptionTrackerCallback.class);
+        // Capture CarrierConfigChangeListener to emulate the carrier config change notification
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
         mTelephonySubscriptionTracker =
                 new TelephonySubscriptionTracker(mContext, mHandler, mCallback, mDeps);
         mTelephonySubscriptionTracker.register();
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                listenerArgumentCaptor.capture());
+        mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
 
         doReturn(true).when(mDeps).isConfigForIdentifiedCarrier(any());
         doReturn(Arrays.asList(TEST_SUBINFO_1, TEST_SUBINFO_2))
@@ -239,14 +244,11 @@
         return intent;
     }
 
-    private Intent buildTestBroadcastIntent(boolean hasValidSubscription) {
-        Intent intent = new Intent(ACTION_CARRIER_CONFIG_CHANGED);
-        intent.putExtra(EXTRA_SLOT_INDEX, TEST_SIM_SLOT_INDEX);
-        intent.putExtra(
-                EXTRA_SUBSCRIPTION_INDEX,
-                hasValidSubscription ? TEST_SUBSCRIPTION_ID_1 : INVALID_SUBSCRIPTION_ID);
-
-        return intent;
+    private void sendCarrierConfigChange(boolean hasValidSubscription) {
+        mCarrierConfigChangeListener.onCarrierConfigChanged(
+                TEST_SIM_SLOT_INDEX,
+                hasValidSubscription ? TEST_SUBSCRIPTION_ID_1 : INVALID_SUBSCRIPTION_ID,
+                TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
     }
 
     private TelephonySubscriptionSnapshot buildExpectedSnapshot(
@@ -302,14 +304,15 @@
                         any(),
                         eq(mHandler));
         final IntentFilter filter = getIntentFilter();
-        assertEquals(2, filter.countActions());
-        assertTrue(filter.hasAction(ACTION_CARRIER_CONFIG_CHANGED));
+        assertEquals(1, filter.countActions());
         assertTrue(filter.hasAction(ACTION_MULTI_SIM_CONFIG_CHANGED));
 
         verify(mSubscriptionManager)
                 .addOnSubscriptionsChangedListener(any(HandlerExecutor.class), any());
         assertNotNull(getOnSubscriptionsChangedListener());
 
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(), any());
+
         verify(mTelephonyManager, times(2))
                 .registerCarrierPrivilegesCallback(anyInt(), any(HandlerExecutor.class), any());
         verify(mTelephonyManager)
@@ -442,7 +445,7 @@
 
     @Test
     public void testReceiveBroadcast_ConfigReadyWithSubscriptions() throws Exception {
-        mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+        sendCarrierConfigChange(true /* hasValidSubscription */);
         mTestLooper.dispatchAll();
 
         verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
@@ -454,7 +457,7 @@
                 .when(mSubscriptionManager)
                 .getAllSubscriptionInfoList();
 
-        mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+        sendCarrierConfigChange(true /* hasValidSubscription */);
         mTestLooper.dispatchAll();
 
         // Expect an empty snapshot
@@ -465,7 +468,7 @@
     public void testReceiveBroadcast_SlotCleared() throws Exception {
         setupReadySubIds();
 
-        mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
+        sendCarrierConfigChange(false /* hasValidSubscription */);
         mTestLooper.dispatchAll();
 
         verifyNoActiveSubscriptions();
@@ -476,7 +479,7 @@
     public void testReceiveBroadcast_ConfigNotReady() throws Exception {
         doReturn(false).when(mDeps).isConfigForIdentifiedCarrier(any());
 
-        mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+        sendCarrierConfigChange(true /* hasValidSubscription */);
         mTestLooper.dispatchAll();
 
         // No interactions expected; config was not loaded
@@ -485,21 +488,21 @@
 
     @Test
     public void testSubscriptionsClearedAfterValidTriggersCallbacks() throws Exception {
-        mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+        sendCarrierConfigChange(true /* hasValidSubscription */);
         mTestLooper.dispatchAll();
         verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
         assertNotNull(
                 mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
 
         doReturn(Collections.emptyList()).when(mSubscriptionManager).getAllSubscriptionInfoList();
-        mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+        sendCarrierConfigChange(true /* hasValidSubscription */);
         mTestLooper.dispatchAll();
         verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap(), emptyMap())));
     }
 
     @Test
     public void testCarrierConfigUpdatedAfterValidTriggersCallbacks() throws Exception {
-        mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+        sendCarrierConfigChange(true /* hasValidSubscription */);
         mTestLooper.dispatchAll();
         verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
         reset(mCallback);
@@ -510,12 +513,12 @@
                 new int[] {TRANSPORT_WIFI, TRANSPORT_CELLULAR});
         doReturn(updatedConfig)
                 .when(mCarrierConfigManager)
-                .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1));
+                .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1), any());
 
         Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap = new HashMap<>();
         subIdToCarrierConfigMap.put(
                 TEST_SUBSCRIPTION_ID_1, new PersistableBundleWrapper(updatedConfig));
-        mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+        sendCarrierConfigChange(true /* hasValidSubscription */);
         mTestLooper.dispatchAll();
 
         verify(mCallback)
@@ -530,13 +533,13 @@
 
     @Test
     public void testSlotClearedAfterValidTriggersCallbacks() throws Exception {
-        mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+        sendCarrierConfigChange(true /* hasValidSubscription */);
         mTestLooper.dispatchAll();
         verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
         assertNotNull(
                 mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
 
-        mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
+        sendCarrierConfigChange(false /* hasValidSubscription */);
         mTestLooper.dispatchAll();
         verify(mCallback)
                 .onNewSnapshot(