Merge "Revert "Properly notify batterystats when packages change idleness.""
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/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 43f7279..76336fb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -2545,6 +2545,7 @@
         final JobStatus rescheduledJob = needsReschedule
                 ? getRescheduleJobForFailureLocked(jobStatus, debugStopReason) : null;
         if (rescheduledJob != null
+                && !rescheduledJob.shouldTreatAsUserInitiatedJob()
                 && (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT
                 || debugStopReason == JobParameters.INTERNAL_STOP_REASON_PREEMPT)) {
             rescheduledJob.disallowRunInBatterySaverAndDoze();
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/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index a2e8eb4..9a69fdf 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -1411,16 +1411,18 @@
     public boolean canRunInDoze() {
         return appHasDozeExemption
                 || (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0
-                || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
                 || shouldTreatAsUserInitiatedJob()
-                && (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0);
+                // EJs can't run in Doze if we explicitly require that the device is not Dozing.
+                || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
+                        && (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0);
     }
 
     boolean canRunInBatterySaver() {
         return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0
-                || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
                 || shouldTreatAsUserInitiatedJob()
-                && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0);
+                // EJs can't run in Battery Saver if we explicitly require that Battery Saver is off
+                || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
+                        && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0);
     }
 
     /** @return true if the constraint was changed, false otherwise. */
diff --git a/core/api/current.txt b/core/api/current.txt
index c0e0eab..a91607a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4319,6 +4319,7 @@
     method public void recreate();
     method public void registerActivityLifecycleCallbacks(@NonNull android.app.Application.ActivityLifecycleCallbacks);
     method public void registerForContextMenu(android.view.View);
+    method @RequiresPermission(android.Manifest.permission.DETECT_SCREEN_CAPTURE) public void registerScreenCaptureCallback(@NonNull java.util.concurrent.Executor, @NonNull android.app.Activity.ScreenCaptureCallback);
     method public boolean releaseInstance();
     method @Deprecated public final void removeDialog(int);
     method public void reportFullyDrawn();
@@ -4404,6 +4405,7 @@
     method public void triggerSearch(String, @Nullable android.os.Bundle);
     method public void unregisterActivityLifecycleCallbacks(@NonNull android.app.Application.ActivityLifecycleCallbacks);
     method public void unregisterForContextMenu(android.view.View);
+    method @RequiresPermission(android.Manifest.permission.DETECT_SCREEN_CAPTURE) public void unregisterScreenCaptureCallback(@NonNull android.app.Activity.ScreenCaptureCallback);
     field public static final int DEFAULT_KEYS_DIALER = 1; // 0x1
     field public static final int DEFAULT_KEYS_DISABLE = 0; // 0x0
     field public static final int DEFAULT_KEYS_SEARCH_GLOBAL = 4; // 0x4
@@ -4417,6 +4419,10 @@
     field public static final int RESULT_OK = -1; // 0xffffffff
   }
 
+  public static interface Activity.ScreenCaptureCallback {
+    method public void onScreenCaptured();
+  }
+
   @Deprecated public class ActivityGroup extends android.app.Activity {
     ctor @Deprecated public ActivityGroup();
     ctor @Deprecated public ActivityGroup(boolean);
@@ -12881,16 +12887,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);
@@ -19045,15 +19041,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
@@ -20912,6 +20908,7 @@
     method public void adjustStreamVolume(int, int, int);
     method public void adjustSuggestedStreamVolume(int, int, int);
     method public void adjustVolume(int, int);
+    method public void adjustVolumeGroupVolume(int, int, int);
     method public void clearCommunicationDevice();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public boolean clearPreferredMixerAttributes(@NonNull android.media.AudioAttributes, @NonNull android.media.AudioDeviceInfo);
     method public void dispatchMediaKeyEvent(android.view.KeyEvent);
@@ -20942,6 +20939,7 @@
     method public float getStreamVolumeDb(int, int, int);
     method @NonNull public java.util.List<android.media.AudioMixerAttributes> getSupportedMixerAttributes(@NonNull android.media.AudioDeviceInfo);
     method @Deprecated public int getVibrateSetting(int);
+    method public int getVolumeGroupIdForAttributes(@NonNull android.media.AudioAttributes);
     method @Deprecated public boolean isBluetoothA2dpOn();
     method public boolean isBluetoothScoAvailableOffCall();
     method @Deprecated public boolean isBluetoothScoOn();
@@ -20955,6 +20953,7 @@
     method public boolean isStreamMute(int);
     method public boolean isSurroundFormatEnabled(int);
     method public boolean isVolumeFixed();
+    method public boolean isVolumeGroupMuted(int);
     method @Deprecated public boolean isWiredHeadsetOn();
     method public void loadSoundEffects();
     method public void playSoundEffect(int);
@@ -24036,6 +24035,7 @@
     method public void registerTransferCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.TransferCallback);
     method public void setOnGetControllerHintsListener(@Nullable android.media.MediaRouter2.OnGetControllerHintsListener);
     method public void setRouteListingPreference(@Nullable android.media.RouteListingPreference);
+    method public void showSystemOutputSwitcher();
     method public void stop();
     method public void transferTo(@NonNull android.media.MediaRoute2Info);
     method public void unregisterControllerCallback(@NonNull android.media.MediaRouter2.ControllerCallback);
@@ -33225,7 +33225,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);
@@ -35376,7 +35375,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;
@@ -35397,7 +35396,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";
@@ -35430,7 +35429,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";
@@ -35476,7 +35475,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";
@@ -35495,7 +35494,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";
@@ -35542,7 +35541,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";
@@ -35566,7 +35565,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";
@@ -35597,7 +35596,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";
@@ -52963,10 +52962,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 {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d3775ad..a1671ab 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 {
@@ -4588,8 +4596,7 @@
   }
 
   public final class HdmiPortInfo implements android.os.Parcelable {
-    ctor public HdmiPortInfo(int, int, int, boolean, boolean, boolean);
-    ctor public HdmiPortInfo(int, int, int, boolean, boolean, boolean, boolean);
+    ctor @Deprecated public HdmiPortInfo(int, int, int, boolean, boolean, boolean);
     method public int describeContents();
     method public int getAddress();
     method public int getId();
@@ -4604,6 +4611,15 @@
     field public static final int PORT_OUTPUT = 1; // 0x1
   }
 
+  public static final class HdmiPortInfo.Builder {
+    ctor public HdmiPortInfo.Builder(int, int, int);
+    method @NonNull public android.hardware.hdmi.HdmiPortInfo build();
+    method @NonNull public android.hardware.hdmi.HdmiPortInfo.Builder setArcSupported(boolean);
+    method @NonNull public android.hardware.hdmi.HdmiPortInfo.Builder setCecSupported(boolean);
+    method @NonNull public android.hardware.hdmi.HdmiPortInfo.Builder setEarcSupported(boolean);
+    method @NonNull public android.hardware.hdmi.HdmiPortInfo.Builder setMhlSupported(boolean);
+  }
+
   public abstract class HdmiRecordListener {
     ctor public HdmiRecordListener();
     method public void onClearTimerRecordingResult(int, int);
@@ -6672,6 +6688,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
     method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int getLastAudibleStreamVolume(int);
+    method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int getLastAudibleVolumeGroupVolume(int);
     method @IntRange(from=0) public long getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
@@ -6681,6 +6698,9 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
+    method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int getVolumeGroupMaxVolumeIndex(int);
+    method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int getVolumeGroupMinVolumeIndex(int);
+    method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int getVolumeGroupVolumeIndex(int);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method public boolean isAudioServerRunning();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean isBluetoothVariableLatencyEnabled();
@@ -6714,6 +6734,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public void setVolumeGroupVolumeIndex(int, int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean supportsBluetoothVariableLatency();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -7120,6 +7141,7 @@
     method public int describeContents();
     method @NonNull public android.media.AudioAttributes getAudioAttributes();
     method public int getId();
+    method @NonNull public String getName();
     method public boolean supportsAudioAttributes(@NonNull android.media.AudioAttributes);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioProductStrategy> CREATOR;
@@ -8759,6 +8781,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 +8804,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 +8852,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 +8902,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 +10502,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 +10511,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();
@@ -16566,6 +16654,7 @@
 
   public interface WindowManager extends android.view.ViewManager {
     method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public android.graphics.Region getCurrentImeTouchRegion();
+    method @NonNull public default java.util.List<android.content.ComponentName> notifyScreenshotListeners(int);
     method public default void registerTaskFpsCallback(@IntRange(from=0) int, @NonNull java.util.concurrent.Executor, @NonNull android.window.TaskFpsCallback);
     method public default void unregisterTaskFpsCallback(@NonNull android.window.TaskFpsCallback);
   }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index da9697b..8dc7dc3 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -946,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 {
@@ -975,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 {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index c0239e8..3c17a33 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.DETECT_SCREEN_CAPTURE;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -26,6 +27,7 @@
 import static java.lang.Character.MIN_VALUE;
 
 import android.annotation.CallSuper;
+import android.annotation.CallbackExecutor;
 import android.annotation.DrawableRes;
 import android.annotation.IdRes;
 import android.annotation.IntDef;
@@ -1016,6 +1018,7 @@
     private ComponentCallbacksController mCallbacksController;
 
     @Nullable private IVoiceInteractionManagerService mVoiceInteractionManagerService;
+    private ScreenCaptureCallbackHandler mScreenCaptureCallbackHandler;
 
     private final WindowControllerCallback mWindowControllerCallback =
             new WindowControllerCallback() {
@@ -9222,4 +9225,43 @@
         }
         return mWindow.getOnBackInvokedDispatcher();
     }
+
+    /**
+     * Interface for observing screen captures of an {@link Activity}.
+     */
+    public interface ScreenCaptureCallback {
+        /**
+         * Called when one of the monitored activities is captured.
+         * This is not invoked if the activity window
+         * has {@link WindowManager.LayoutParams#FLAG_SECURE} set.
+         */
+        void onScreenCaptured();
+    }
+
+    /**
+     * Registers a screen capture callback for this activity.
+     * The callback will be triggered when a screen capture of this activity is attempted.
+     * This callback will be executed on the thread of the passed {@code executor}.
+     * For details, see {@link ScreenCaptureCallback#onScreenCaptured}.
+     */
+    @RequiresPermission(DETECT_SCREEN_CAPTURE)
+    public void registerScreenCaptureCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull ScreenCaptureCallback callback) {
+        if (mScreenCaptureCallbackHandler == null) {
+            mScreenCaptureCallbackHandler = new ScreenCaptureCallbackHandler(mToken);
+        }
+        mScreenCaptureCallbackHandler.registerScreenCaptureCallback(executor, callback);
+    }
+
+
+    /**
+     * Unregisters a screen capture callback for this surface.
+     */
+    @RequiresPermission(DETECT_SCREEN_CAPTURE)
+    public void unregisterScreenCaptureCallback(@NonNull ScreenCaptureCallback callback) {
+        if (mScreenCaptureCallbackHandler != null) {
+            mScreenCaptureCallbackHandler.unregisterScreenCaptureCallback(callback);
+        }
+    }
 }
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 461aa3c..e97e711 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -28,6 +28,7 @@
 import android.app.IAssistDataReceiver;
 import android.app.IInstrumentationWatcher;
 import android.app.IProcessObserver;
+import android.app.IScreenCaptureObserver;
 import android.app.IServiceConnection;
 import android.app.IStopUserCallback;
 import android.app.ITaskStackListener;
@@ -354,4 +355,23 @@
      */
     android.window.BackNavigationInfo startBackNavigation(
             in IWindowFocusObserver focusObserver, in BackAnimationAdapter adaptor);
+
+    /**
+     * registers a callback to be invoked when the screen is captured.
+     *
+     * @param observer callback to be registered.
+     * @param activityToken The token for the activity to set the callback to.
+     * @hide
+     */
+    void registerScreenCaptureObserver(IBinder activityToken, IScreenCaptureObserver observer);
+
+    /**
+     * unregisters the screen capture callback which was registered with
+     * {@link #registerScreenCaptureObserver(ScreenCaptureObserver)}.
+     *
+     * @param observer callback to be unregistered.
+     * @param activityToken The token for the activity to unset the callback from.
+     * @hide
+     */
+    void unregisterScreenCaptureObserver(IBinder activityToken, IScreenCaptureObserver observer);
 }
diff --git a/core/java/android/app/IScreenCaptureObserver.aidl b/core/java/android/app/IScreenCaptureObserver.aidl
new file mode 100644
index 0000000..1668e5d10
--- /dev/null
+++ b/core/java/android/app/IScreenCaptureObserver.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package android.app;
+
+/** {@hide} */
+interface IScreenCaptureObserver {
+    oneway void onScreenCaptured();
+}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 20869e0..6206f31 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -88,6 +88,7 @@
 per-file *Task* = file:/services/core/java/com/android/server/wm/OWNERS
 per-file Window* = file:/services/core/java/com/android/server/wm/OWNERS
 per-file ConfigurationController.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *ScreenCapture* = file:/services/core/java/com/android/server/wm/OWNERS
 
 # TODO(b/174932174): determine the ownership of KeyguardManager.java
 
diff --git a/core/java/android/app/ScreenCaptureCallbackHandler.java b/core/java/android/app/ScreenCaptureCallbackHandler.java
new file mode 100644
index 0000000..997cf51
--- /dev/null
+++ b/core/java/android/app/ScreenCaptureCallbackHandler.java
@@ -0,0 +1,118 @@
+/*
+ * 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.app;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+
+import java.util.concurrent.Executor;
+
+/** Handles screen capture callbacks.
+ * @hide
+ **/
+public class ScreenCaptureCallbackHandler {
+
+    private final IBinder mActivityToken;
+    private final ScreenCaptureObserver mObserver;
+    private final ArrayMap<Activity.ScreenCaptureCallback, ScreenCaptureRegistration>
+            mScreenCaptureRegistrations = new ArrayMap<>();
+
+    public ScreenCaptureCallbackHandler(@NonNull IBinder activityToken) {
+        mActivityToken = activityToken;
+        mObserver = new ScreenCaptureObserver(mScreenCaptureRegistrations);
+    }
+
+    private static class ScreenCaptureRegistration {
+        Executor mExecutor;
+        Activity.ScreenCaptureCallback mCallback;
+
+        ScreenCaptureRegistration(Executor executor, Activity.ScreenCaptureCallback callback) {
+            this.mExecutor = executor;
+            this.mCallback = callback;
+        }
+    }
+
+    private static class ScreenCaptureObserver extends IScreenCaptureObserver.Stub {
+        ArrayMap<Activity.ScreenCaptureCallback, ScreenCaptureRegistration> mRegistrations;
+
+        ScreenCaptureObserver(
+                ArrayMap<Activity.ScreenCaptureCallback, ScreenCaptureRegistration>
+                        registrations) {
+            this.mRegistrations = registrations;
+        }
+
+        @Override
+        public void onScreenCaptured() {
+            for (ScreenCaptureRegistration registration : mRegistrations.values()) {
+                registration.mExecutor.execute(
+                        () -> {
+                            registration.mCallback.onScreenCaptured();
+                        });
+            }
+        }
+    }
+
+    /**
+     * Start monitoring for screen captures of the activity, the callback will be triggered whenever
+     * a screen capture is attempted. This callback will be executed on the thread of the passed
+     * {@code executor}. If the window is FLAG_SECURE, the callback will not be triggered.
+     */
+    public void registerScreenCaptureCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Activity.ScreenCaptureCallback callback) {
+        ScreenCaptureRegistration registration =
+                new ScreenCaptureRegistration(executor, callback);
+        synchronized (mScreenCaptureRegistrations) {
+            if (mScreenCaptureRegistrations.containsKey(callback)) {
+                throw new IllegalStateException(
+                        "Capture observer already registered with the activity");
+            }
+            mScreenCaptureRegistrations.put(callback, registration);
+            // register with system server only once.
+            if (mScreenCaptureRegistrations.size() == 1) {
+                try {
+                    ActivityTaskManager.getService()
+                            .registerScreenCaptureObserver(mActivityToken, mObserver);
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
+    /** Stop monitoring for screen captures of the activity */
+    public void unregisterScreenCaptureCallback(@NonNull Activity.ScreenCaptureCallback callback) {
+        synchronized (mScreenCaptureRegistrations) {
+            if (!mScreenCaptureRegistrations.containsKey(callback)) {
+                throw new IllegalStateException(
+                        "Capture observer not registered with the activity");
+            }
+            mScreenCaptureRegistrations.remove(callback);
+            // unregister only if no more registrations are left
+            if (mScreenCaptureRegistrations.size() == 0) {
+                try {
+                    ActivityTaskManager.getService().unregisterScreenCaptureObserver(mActivityToken,
+                            mObserver);
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
+}
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/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 6ca708a..a408ea6 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -388,7 +388,7 @@
      *
      * <ul>
      *     <li>
-     *         The type has a 1 minute timeout.
+     *         The type has a 3 minute timeout.
      *         A foreground service of this type must be stopped within the timeout by
      *         {@link android.app.Service#stopSelf),
      *         or {@link android.content.Context#stopService).
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/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java
index 5bb1f03..5615418 100644
--- a/core/java/android/hardware/hdmi/HdmiPortInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java
@@ -15,14 +15,20 @@
  */
 package android.hardware.hdmi;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import androidx.annotation.IntRange;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
- * A class to encapsulate HDMI port information. Contains the capability of the ports such as
+ * Encapsulates HDMI port information. Contains the capability of the ports such as
  * HDMI-CEC, MHL, ARC(Audio Return Channel), eARC and physical address assigned to each port.
  *
  * @hide
@@ -35,6 +41,18 @@
     /** HDMI port type: Output */
     public static final int PORT_OUTPUT = 1;
 
+    /**
+     * @hide
+     *
+     * @see HdmiPortInfo#getType()
+     */
+    @IntDef(prefix = { "PORT_" }, value = {
+            PORT_INPUT,
+            PORT_OUTPUT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PortType {}
+
     private final int mId;
     private final int mType;
     private final int mAddress;
@@ -46,43 +64,48 @@
     /**
      * Constructor.
      *
-     * @param id identifier assigned to each port. 1 for HDMI port 1
+     * @param id identifier assigned to each port. 1 for HDMI OUT port 1
      * @param type HDMI port input/output type
      * @param address physical address of the port
      * @param cec {@code true} if HDMI-CEC is supported on the port
      * @param mhl {@code true} if MHL is supported on the port
      * @param arc {@code true} if audio return channel is supported on the port
-     */
-    public HdmiPortInfo(int id, int type, int address, boolean cec, boolean mhl, boolean arc) {
-        this(id, type, address, cec, mhl, arc, false);
-    }
-
-    /**
-     * Constructor.
      *
-     * @param id identifier assigned to each port. 1 for HDMI port 1
-     * @param type HDMI port input/output type
-     * @param address physical address of the port
-     * @param cec {@code true} if HDMI-CEC is supported on the port
-     * @param mhl {@code true} if MHL is supported on the port
-     * @param arc {@code true} if audio return channel is supported on the port
-     * @param earc {@code true} if eARC is supported on the port
+     * @deprecated use {@link Builder()} instead
      */
-    public HdmiPortInfo(int id, int type, int address,
-            boolean cec, boolean mhl, boolean arc, boolean earc) {
+    @Deprecated
+    public HdmiPortInfo(int id, @PortType int type,
+            @IntRange(from = 0) int address, boolean cec, boolean mhl, boolean arc) {
         mId = id;
         mType = type;
         mAddress = address;
         mCecSupported = cec;
         mArcSupported = arc;
-        mEarcSupported = earc;
+        mEarcSupported = false;
         mMhlSupported = mhl;
     }
 
     /**
-     * Returns the port id.
+     * Converts an instance to a builder
      *
-     * @return port id
+     * @hide
+     */
+    public Builder toBuilder() {
+        return new Builder(this);
+    }
+
+    private HdmiPortInfo(Builder builder) {
+        this.mId = builder.mId;
+        this.mType = builder.mType;
+        this.mAddress = builder.mAddress;
+        this.mCecSupported = builder.mCecSupported;
+        this.mArcSupported = builder.mArcSupported;
+        this.mEarcSupported = builder.mEarcSupported;
+        this.mMhlSupported = builder.mMhlSupported;
+    }
+
+    /**
+     * Returns the port id.
      */
     public int getId() {
         return mId;
@@ -90,26 +113,22 @@
 
     /**
      * Returns the port type.
-     *
-     * @return port type
      */
+    @PortType
     public int getType() {
         return mType;
     }
 
     /**
      * Returns the port address.
-     *
-     * @return port address
      */
+    @IntRange(from = 0)
     public int getAddress() {
         return mAddress;
     }
 
     /**
      * Returns {@code true} if the port supports HDMI-CEC signaling.
-     *
-     * @return {@code true} if the port supports HDMI-CEC signaling.
      */
     public boolean isCecSupported() {
         return mCecSupported;
@@ -117,8 +136,6 @@
 
     /**
      * Returns {@code true} if the port supports MHL signaling.
-     *
-     * @return {@code true} if the port supports MHL signaling.
      */
     public boolean isMhlSupported() {
         return mMhlSupported;
@@ -126,8 +143,6 @@
 
     /**
      * Returns {@code true} if the port supports audio return channel.
-     *
-     * @return {@code true} if the port supports audio return channel
      */
     public boolean isArcSupported() {
         return mArcSupported;
@@ -135,8 +150,6 @@
 
     /**
      * Returns {@code true} if the port supports eARC.
-     *
-     * @return {@code true} if the port supports eARC.
      */
     public boolean isEarcSupported() {
         return mEarcSupported;
@@ -166,7 +179,12 @@
                     boolean arc = (source.readInt() == 1);
                     boolean mhl = (source.readInt() == 1);
                     boolean earc = (source.readInt() == 1);
-                    return new HdmiPortInfo(id, type, address, cec, mhl, arc, earc);
+                    return new Builder(id, type, address)
+                            .setCecSupported(cec)
+                            .setArcSupported(arc)
+                            .setEarcSupported(earc)
+                            .setMhlSupported(mhl)
+                            .build();
                 }
 
                 @Override
@@ -225,4 +243,95 @@
         return java.util.Objects.hash(
                 mId, mType, mAddress, mCecSupported, mArcSupported, mMhlSupported, mEarcSupported);
     }
+
+    /**
+     * Builder for {@link HdmiPortInfo} instances.
+     */
+    public static final class Builder {
+        // Required parameters
+        private int mId;
+        private int mType;
+        private int mAddress;
+
+        // Optional parameters that are set to false by default.
+        private boolean mCecSupported;
+        private boolean mArcSupported;
+        private boolean mEarcSupported;
+        private boolean mMhlSupported;
+
+        /**
+         * Constructor
+         *
+         * @param id      identifier assigned to each port. 1 for HDMI OUT port 1
+         * @param type    HDMI port input/output type
+         * @param address physical address of the port
+         * @throws IllegalArgumentException if the parameters are invalid
+         */
+        public Builder(int id, @PortType int type, @IntRange(from = 0) int address) {
+            if (type != PORT_INPUT && type != PORT_OUTPUT) {
+                throw new IllegalArgumentException(
+                        "type should be " + PORT_INPUT + " or " + PORT_OUTPUT + ".");
+            }
+            if (address < 0) {
+                throw new IllegalArgumentException("address should be positive.");
+            }
+            mId = id;
+            mType = type;
+            mAddress = address;
+        }
+
+        private Builder(@NonNull HdmiPortInfo hdmiPortInfo) {
+            mId = hdmiPortInfo.mId;
+            mType = hdmiPortInfo.mType;
+            mAddress = hdmiPortInfo.mAddress;
+            mCecSupported = hdmiPortInfo.mCecSupported;
+            mArcSupported = hdmiPortInfo.mArcSupported;
+            mEarcSupported = hdmiPortInfo.mEarcSupported;
+            mMhlSupported = hdmiPortInfo.mMhlSupported;
+        }
+
+        /**
+         * Create a new {@link HdmiPortInfo} object.
+         */
+        @NonNull
+        public HdmiPortInfo build() {
+            return new HdmiPortInfo(this);
+        }
+
+        /**
+         * Sets the value for whether the port supports HDMI-CEC signaling.
+         */
+        @NonNull
+        public Builder setCecSupported(boolean supported) {
+            mCecSupported = supported;
+            return this;
+        }
+
+        /**
+         * Sets the value for whether the port supports audio return channel.
+         */
+        @NonNull
+        public Builder setArcSupported(boolean supported) {
+            mArcSupported = supported;
+            return this;
+        }
+
+        /**
+         * Sets the value for whether the port supports eARC.
+         */
+        @NonNull
+        public Builder setEarcSupported(boolean supported) {
+            mEarcSupported = supported;
+            return this;
+        }
+
+        /**
+         * Sets the value for whether the port supports MHL signaling.
+         */
+        @NonNull
+        public Builder setMhlSupported(boolean supported) {
+            mMhlSupported = supported;
+            return this;
+        }
+    }
 }
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/hardware/soundtrigger/OWNERS b/core/java/android/hardware/soundtrigger/OWNERS
index e5d0370..01b2cb9 100644
--- a/core/java/android/hardware/soundtrigger/OWNERS
+++ b/core/java/android/hardware/soundtrigger/OWNERS
@@ -1,2 +1,2 @@
-ytai@google.com
+atneya@google.com
 elaurent@google.com
diff --git a/core/java/android/hardware/usb/DisplayPortAltModeInfo.java b/core/java/android/hardware/usb/DisplayPortAltModeInfo.java
index febc643..ef2fefc 100644
--- a/core/java/android/hardware/usb/DisplayPortAltModeInfo.java
+++ b/core/java/android/hardware/usb/DisplayPortAltModeInfo.java
@@ -39,6 +39,8 @@
     private final @DisplayPortAltModeStatus int mPartnerSinkStatus;
     private final @DisplayPortAltModeStatus int mCableStatus;
     private final int mNumLanes;
+    private final boolean mHpd;
+    private final int mLinkTrainingStatus;
 
     /**
      * Port Partners:
@@ -103,14 +105,18 @@
         mPartnerSinkStatus = DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN;
         mCableStatus = DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN;
         mNumLanes = 0;
+        mHpd = false;
+        mLinkTrainingStatus = 0;
     }
 
     /** @hide */
     public DisplayPortAltModeInfo(int partnerSinkStatus, int cableStatus,
-            int numLanes) {
+            int numLanes, boolean hpd, int linkTrainingStatus) {
         mPartnerSinkStatus = partnerSinkStatus;
         mCableStatus = cableStatus;
         mNumLanes = numLanes;
+        mHpd = hpd;
+        mLinkTrainingStatus = linkTrainingStatus;
     }
 
     /**
@@ -155,6 +161,8 @@
         dest.writeInt(mPartnerSinkStatus);
         dest.writeInt(mCableStatus);
         dest.writeInt(mNumLanes);
+        dest.writeBoolean(mHpd);
+        dest.writeInt(mLinkTrainingStatus);
     }
 
     @NonNull
@@ -166,6 +174,10 @@
                 + mCableStatus
                 + " numLanes="
                 + mNumLanes
+                + " hpd="
+                + mHpd
+                + " linkTrainingStatus="
+                + mLinkTrainingStatus
                 + "}";
     }
 
@@ -195,7 +207,10 @@
             int partnerSinkStatus = in.readInt();
             int cableStatus = in.readInt();
             int numLanes = in.readInt();
-            return new DisplayPortAltModeInfo(partnerSinkStatus, cableStatus, numLanes);
+            boolean hpd = in.readBoolean();
+            int linkTrainingStatus = in.readInt();
+            return new DisplayPortAltModeInfo(partnerSinkStatus, cableStatus, numLanes, hpd,
+                    linkTrainingStatus);
         }
 
         @Override
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/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 70b04a1..06fe523 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -166,6 +166,13 @@
     public static final String SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS =
             "settings_show_udfps_enroll_in_settings";
 
+    /**
+     * Flag to enable lock screen credentials transfer API in Android U.
+     * @hide
+     */
+    public static final String SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API =
+            "settings_enable_lockscreen_transfer_api";
+
     private static final Map<String, String> DEFAULT_FLAGS;
 
     static {
@@ -207,6 +214,7 @@
         DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "false");
         DEFAULT_FLAGS.put(SETTINGS_FLASH_ALERTS, "false");
         DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "false");
+        DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "false");
     }
 
     private static final Set<String> PERSISTENT_FLAGS;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9fdda29..4f3c5fe 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -22,6 +22,7 @@
 import com.android.internal.policy.IShortcutService;
 
 import android.app.IAssistDataReceiver;
+import android.content.ComponentName;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
@@ -1000,4 +1001,14 @@
      * Mark a SurfaceSyncGroup stored in WindowManager as ready.
      */
     oneway void markSurfaceSyncGroupReady(in IBinder syncGroupToken);
+
+    /**
+     * Invoked when a screenshot is taken of the default display to notify registered listeners.
+     *
+     * Should be invoked only by SysUI.
+     *
+     * @param displayId id of the display screenshot.
+     * @return List of ComponentNames corresponding to the activities that were notified.
+    */
+    List<ComponentName> notifyScreenshotListeners(int displayId);
 }
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/WindowManager.java b/core/java/android/view/WindowManager.java
index e437c1f..5f6f55a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -94,6 +94,7 @@
 import android.app.Presentation;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ClipData;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
@@ -5269,4 +5270,18 @@
     default Bitmap snapshotTaskForRecents(@IntRange(from = 0) int taskId) {
         return null;
     }
+
+    /**
+     * Invoked when a screenshot is taken of the default display to notify registered listeners.
+     *
+     * Should be invoked only by SysUI.
+     *
+     * @param displayId id of the display screenshot.
+     * @return List of ComponentNames corresponding to the activities that were notified.
+     * @hide
+     */
+    @SystemApi
+    default @NonNull List<ComponentName> notifyScreenshotListeners(int displayId) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 19d49d9..7ea8c0c 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -26,6 +26,7 @@
 import android.annotation.Nullable;
 import android.annotation.UiContext;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Region;
@@ -412,4 +413,15 @@
     IBinder getDefaultToken() {
         return mDefaultToken;
     }
+
+    @Override
+    @NonNull
+    public List<ComponentName> notifyScreenshotListeners(int displayId) {
+        try {
+            return List.copyOf(WindowManagerGlobal.getWindowManagerService()
+                    .notifyScreenshotListeners(displayId));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
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/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/expresslog/Counter.java b/core/java/com/android/internal/expresslog/Counter.java
index 7571073..cc37c69 100644
--- a/core/java/com/android/internal/expresslog/Counter.java
+++ b/core/java/com/android/internal/expresslog/Counter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 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.
@@ -39,9 +39,7 @@
      * @hide
      */
     public static void logIncrement(@NonNull String metricId, long amount) {
-        final long metricIdHash = hashString(metricId);
+        final long metricIdHash = Utils.hashString(metricId);
         FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_EVENT_REPORTED, metricIdHash, amount);
     }
-
-    private static native long hashString(String stringToHash);
 }
diff --git a/core/java/com/android/internal/expresslog/Utils.java b/core/java/com/android/internal/expresslog/Utils.java
new file mode 100644
index 0000000..d82192f
--- /dev/null
+++ b/core/java/com/android/internal/expresslog/Utils.java
@@ -0,0 +1,21 @@
+/*
+ * 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.internal.expresslog;
+
+final class Utils {
+    static native long hashString(String stringToHash);
+}
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/jni/Android.bp b/core/jni/Android.bp
index 91d2ecb..eca85ff 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -219,7 +219,7 @@
                 "android_security_Scrypt.cpp",
                 "com_android_internal_content_om_OverlayConfig.cpp",
                 "com_android_internal_content_om_OverlayManagerImpl.cpp",
-                "com_android_internal_expresslog_Counter.cpp",
+                "com_android_internal_expresslog_Utils.cpp",
                 "com_android_internal_net_NetworkUtilsInternal.cpp",
                 "com_android_internal_os_ClassLoaderFactory.cpp",
                 "com_android_internal_os_FuseAppLoop.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 578cf24..b550f28 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -200,7 +200,7 @@
 extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
 extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env);
 extern int register_com_android_internal_content_om_OverlayManagerImpl(JNIEnv* env);
-extern int register_com_android_internal_expresslog_Counter(JNIEnv* env);
+extern int register_com_android_internal_expresslog_Utils(JNIEnv* env);
 extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env);
 extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
 extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
@@ -1586,7 +1586,7 @@
         REG_JNI(register_android_os_incremental_IncrementalManager),
         REG_JNI(register_com_android_internal_content_om_OverlayConfig),
         REG_JNI(register_com_android_internal_content_om_OverlayManagerImpl),
-        REG_JNI(register_com_android_internal_expresslog_Counter),
+        REG_JNI(register_com_android_internal_expresslog_Utils),
         REG_JNI(register_com_android_internal_net_NetworkUtilsInternal),
         REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
         REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter),
diff --git a/core/jni/com_android_internal_expresslog_Counter.cpp b/core/jni/com_android_internal_expresslog_Utils.cpp
similarity index 83%
rename from core/jni/com_android_internal_expresslog_Counter.cpp
rename to core/jni/com_android_internal_expresslog_Utils.cpp
index d4a8c23..d33a7bd 100644
--- a/core/jni/com_android_internal_expresslog_Counter.cpp
+++ b/core/jni/com_android_internal_expresslog_Utils.cpp
@@ -26,7 +26,7 @@
 static jclass g_stringClass = nullptr;
 
 /**
- * Class:     com_android_internal_expresslog_Counter
+ * Class:     com_android_internal_expresslog_Utils
  * Method:    hashString
  * Signature: (Ljava/lang/String;)J
  */
@@ -43,15 +43,15 @@
         {"hashString", "(Ljava/lang/String;)J", (void*)hashString},
 };
 
-static const char* const kCounterPathName = "com/android/internal/expresslog/Counter";
+static const char* const kUtilsPathName = "com/android/internal/expresslog/Utils";
 
 namespace android {
 
-int register_com_android_internal_expresslog_Counter(JNIEnv* env) {
+int register_com_android_internal_expresslog_Utils(JNIEnv* env) {
     jclass stringClass = FindClassOrDie(env, "java/lang/String");
     g_stringClass = MakeGlobalRefOrDie(env, stringClass);
 
-    return RegisterMethodsOrDie(env, kCounterPathName, g_methods, NELEM(g_methods));
+    return RegisterMethodsOrDie(env, kUtilsPathName, g_methods, NELEM(g_methods));
 }
 
 } // namespace android
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java
index 64df348..e82b699 100755
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java
@@ -41,34 +41,58 @@
 
         new EqualsTester()
                 .addEqualityGroup(
-                        new HdmiPortInfo(portId, portType, address, isCec, isMhl, isArcSupported,
-                                isEarcSupported),
-                        new HdmiPortInfo(portId, portType, address, isCec, isMhl, isArcSupported,
-                                isEarcSupported))
+                        new HdmiPortInfo.Builder(portId, portType, address)
+                                .setCecSupported(isCec)
+                                .setMhlSupported(isMhl)
+                                .setArcSupported(isArcSupported)
+                                .setEarcSupported(isEarcSupported),
+                        new HdmiPortInfo.Builder(portId, portType, address)
+                                .setCecSupported(isCec)
+                                .setMhlSupported(isMhl)
+                                .setArcSupported(isArcSupported)
+                                .setEarcSupported(isEarcSupported))
                 .addEqualityGroup(
-                        new HdmiPortInfo(
-                                portId + 1, portType, address, isCec, isMhl, isArcSupported,
-                                isEarcSupported))
+                        new HdmiPortInfo.Builder(portId + 1, portType, address)
+                                .setCecSupported(isCec)
+                                .setMhlSupported(isMhl)
+                                .setArcSupported(isArcSupported)
+                                .setEarcSupported(isEarcSupported))
                 .addEqualityGroup(
-                        new HdmiPortInfo(
-                                portId, portType + 1, address, isCec, isMhl, isArcSupported,
-                                isEarcSupported))
+                        new HdmiPortInfo.Builder(portId, portType + 1, address)
+                                .setCecSupported(isCec)
+                                .setMhlSupported(isMhl)
+                                .setArcSupported(isArcSupported)
+                                .setEarcSupported(isEarcSupported))
                 .addEqualityGroup(
-                        new HdmiPortInfo(
-                                portId, portType, address + 1, isCec, isMhl, isArcSupported,
-                                isEarcSupported))
+                        new HdmiPortInfo.Builder(portId, portType, address + 1)
+                                .setCecSupported(isCec)
+                                .setMhlSupported(isMhl)
+                                .setArcSupported(isArcSupported)
+                                .setEarcSupported(isEarcSupported))
                 .addEqualityGroup(
-                        new HdmiPortInfo(portId, portType, address, !isCec, isMhl, isArcSupported,
-                                isEarcSupported))
+                        new HdmiPortInfo.Builder(portId, portType, address)
+                                .setCecSupported(!isCec)
+                                .setMhlSupported(isMhl)
+                                .setArcSupported(isArcSupported)
+                                .setEarcSupported(isEarcSupported))
                 .addEqualityGroup(
-                        new HdmiPortInfo(portId, portType, address, isCec, !isMhl, isArcSupported,
-                                isEarcSupported))
+                        new HdmiPortInfo.Builder(portId, portType, address)
+                                .setCecSupported(isCec)
+                                .setMhlSupported(!isMhl)
+                                .setArcSupported(isArcSupported)
+                                .setEarcSupported(isEarcSupported))
                 .addEqualityGroup(
-                        new HdmiPortInfo(portId, portType, address, isCec, isMhl, !isArcSupported,
-                                isEarcSupported))
+                        new HdmiPortInfo.Builder(portId, portType, address)
+                                .setCecSupported(isCec)
+                                .setMhlSupported(isMhl)
+                                .setArcSupported(!isArcSupported)
+                                .setEarcSupported(isEarcSupported))
                 .addEqualityGroup(
-                        new HdmiPortInfo(portId, portType, address, isCec, isMhl, isArcSupported,
-                                !isEarcSupported))
+                        new HdmiPortInfo.Builder(portId, portType, address)
+                                .setCecSupported(isCec)
+                                .setMhlSupported(isMhl)
+                                .setArcSupported(isArcSupported)
+                                .setEarcSupported(!isEarcSupported))
                 .testEquals();
     }
 }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index bf7f3bd..a37cb80 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -310,6 +310,7 @@
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
         <!-- Permission required for UiModeManager CTS test -->
         <permission name="android.permission.READ_PROJECTION_STATE"/>
+        <permission name="android.permission.READ_WALLPAPER_INTERNAL"/>
         <permission name="android.permission.READ_WIFI_CREDENTIAL"/>
         <permission name="android.permission.REAL_GET_TASKS"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
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/res/color/letterbox_restart_button_background_ripple.xml b/libs/WindowManager/Shell/res/color/letterbox_restart_button_background_ripple.xml
new file mode 100644
index 0000000..a3ca74f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/letterbox_restart_button_background_ripple.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral1_900" android:alpha="0.6" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/letterbox_restart_dismiss_button_background_ripple.xml b/libs/WindowManager/Shell/res/color/letterbox_restart_dismiss_button_background_ripple.xml
new file mode 100644
index 0000000..a3ca74f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/letterbox_restart_dismiss_button_background_ripple.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral1_900" android:alpha="0.6" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background.xml
new file mode 100644
index 0000000..60f3cfe
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       android:shape="rectangle">
+    <solid android:color="?androidprv:attr/colorAccentPrimaryVariant"/>
+    <corners android:radius="@dimen/letterbox_restart_dialog_button_radius"/>
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml
new file mode 100644
index 0000000..ef97ea1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="@color/letterbox_restart_button_background_ripple">
+    <item android:drawable="@drawable/letterbox_restart_button_background"/>
+</ripple>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_button.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_button.xml
new file mode 100644
index 0000000..c247c6e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_button.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:state_checked="true"
+          android:drawable="@drawable/letterbox_restart_checkbox_checked" />
+    <item android:state_pressed="true"
+          android:drawable="@drawable/letterbox_restart_checkbox_checked" />
+    <item android:state_pressed="false"
+          android:drawable="@drawable/letterbox_restart_checkbox_unchecked" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_checked.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_checked.xml
new file mode 100644
index 0000000..4f97e2c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_checked.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="20dp"
+        android:height="20dp"
+        android:viewportWidth="20"
+        android:viewportHeight="20"
+        android:tint="?android:attr/textColorSecondary">
+    <group
+        android:scaleX="0.83333333333"
+        android:scaleY="0.83333333333"
+        android:translateX="0"
+        android:translateY="0">
+        <path
+            android:fillColor="?android:attr/textColorSecondary"
+            android:pathData="M10.6,16.2 L17.65,9.15 16.25,7.75 10.6,13.4 7.75,10.55 6.35,11.95ZM5,21Q4.175,21 3.587,20.413Q3,19.825 3,19V5Q3,4.175 3.587,3.587Q4.175,3 5,3H19Q19.825,3 20.413,3.587Q21,4.175 21,5V19Q21,19.825 20.413,20.413Q19.825,21 19,21Z"/>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_unchecked.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_unchecked.xml
new file mode 100644
index 0000000..bb14d19
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_unchecked.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="20dp"
+        android:height="20dp"
+        android:viewportWidth="20"
+        android:viewportHeight="20"
+        android:tint="?android:attr/textColorSecondary">
+    <group
+        android:scaleX="0.83333333333"
+        android:scaleY="0.83333333333"
+        android:translateX="0"
+        android:translateY="0">
+        <path
+            android:fillColor="?android:attr/textColorSecondary"
+            android:pathData="M5,21Q4.175,21 3.587,20.413Q3,19.825 3,19V5Q3,4.175 3.587,3.587Q4.175,3 5,3H19Q19.825,3 20.413,3.587Q21,4.175 21,5V19Q21,19.825 20.413,20.413Q19.825,21 19,21ZM5,19H19Q19,19 19,19Q19,19 19,19V5Q19,5 19,5Q19,5 19,5H5Q5,5 5,5Q5,5 5,5V19Q5,19 5,19Q5,19 5,19Z"/>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml
new file mode 100644
index 0000000..e3c18a2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       android:shape="rectangle">
+    <solid android:color="?androidprv:attr/colorSurface"/>
+    <corners android:radius="@dimen/letterbox_restart_dialog_corner_radius"/>
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background.xml
new file mode 100644
index 0000000..af89d41
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       android:shape="rectangle">
+    <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant" android:width="1dp"/>
+    <solid android:color="?androidprv:attr/colorSurface" />
+    <corners android:radius="@dimen/letterbox_restart_dialog_button_radius"/>
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml
new file mode 100644
index 0000000..e32aefc
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="@color/letterbox_restart_dismiss_button_background_ripple">
+    <item android:drawable="@drawable/letterbox_restart_dismiss_button_background"/>
+</ripple>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml
new file mode 100644
index 0000000..5053971
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        android:width="@dimen/letterbox_restart_dialog_title_icon_width"
+        android:height="@dimen/letterbox_restart_dialog_title_icon_height"
+        android:viewportWidth="45"
+        android:viewportHeight="44">
+    <group
+        android:scaleX="0.8"
+        android:scaleY="0.8"
+        android:translateX="8"
+        android:translateY="8">
+        <path
+            android:pathData="M0,36V24.5H3V30.85L10.4,23.45L12.55,25.6L5.15,33H11.5V36H0ZM24.5,36V33H30.85L23.5,25.65L25.65,23.5L33,30.85V24.5H36V36H24.5ZM10.35,12.5L3,5.15V11.5H0V0H11.5V3H5.15L12.5,10.35L10.35,12.5ZM25.65,12.5L23.5,10.35L30.85,3H24.5V0H36V11.5H33V5.15L25.65,12.5Z"
+            android:fillColor="?androidprv:attr/colorAccentPrimaryVariant"/>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_ic_arrows.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_ic_arrows.xml
new file mode 100644
index 0000000..b6e0172
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_ic_arrows.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/letterbox_restart_dialog_title_icon_width"
+        android:height="@dimen/letterbox_restart_dialog_title_icon_height"
+        android:viewportWidth="45"
+        android:viewportHeight="44">
+    <group
+        android:scaleX="0.8"
+        android:scaleY="0.8"
+        android:translateX="8"
+        android:translateY="8">
+        <path
+            android:pathData="M0,36V24.5H3V30.85L10.4,23.45L12.55,25.6L5.15,33H11.5V36H0ZM24.5,36V33H30.85L23.5,25.65L25.65,23.5L33,30.85V24.5H36V36H24.5ZM10.35,12.5L3,5.15V11.5H0V0H11.5V3H5.15L12.5,10.35L10.35,12.5ZM25.65,12.5L23.5,10.35L30.85,3H24.5V0H36V11.5H33V5.15L25.65,12.5Z"
+            android:fillColor="@color/compat_controls_text"/>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml
new file mode 100644
index 0000000..ba9852c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml
@@ -0,0 +1,142 @@
+<!--
+  ~ 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.
+  -->
+<com.android.wm.shell.compatui.RestartDialogLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@android:color/system_neutral1_900">
+
+    <!-- The background of the top-level layout acts as the background dim. -->
+
+    <!--TODO (b/266288912): Resolve overdraw warning -->
+
+    <!-- Vertical margin will be set dynamically since it depends on task bounds.
+         Setting the alpha of the dialog container to 0, since it shouldn't be visible until the
+         enter animation starts. -->
+    <FrameLayout
+        android:id="@+id/letterbox_restart_dialog_container"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="@dimen/letterbox_restart_dialog_margin"
+        android:background="@drawable/letterbox_restart_dialog_background"
+        android:alpha="0"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintWidth_max="@dimen/letterbox_restart_dialog_width">
+
+        <!-- The ScrollView should only wrap the content of the dialog, otherwise the background
+             corner radius will be cut off when scrolling to the top/bottom. -->
+
+        <ScrollView android:layout_width="match_parent"
+                    android:layout_height="wrap_content">
+
+            <LinearLayout
+                android:padding="24dp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center_horizontal"
+                android:orientation="vertical">
+
+                <ImageView
+                    android:importantForAccessibility="no"
+                    android:layout_width="@dimen/letterbox_restart_dialog_title_icon_width"
+                    android:layout_height="@dimen/letterbox_restart_dialog_title_icon_height"
+                    android:src="@drawable/letterbox_restart_header_ic_arrows"/>
+
+                <TextView
+                    android:layout_marginVertical="16dp"
+                    android:id="@+id/letterbox_restart_dialog_title"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/letterbox_restart_dialog_title"
+                    android:textAlignment="center"
+                    android:textAppearance="@style/RestartDialogTitleText"/>
+
+                <TextView
+                    android:textAppearance="@style/RestartDialogBodyText"
+                    android:id="@+id/letterbox_restart_dialog_description"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/letterbox_restart_dialog_description"
+                    android:textAlignment="center"/>
+
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal"
+                    android:layout_gravity="center_vertical"
+                    android:layout_marginVertical="32dp">
+
+                    <CheckBox
+                        android:id="@+id/letterbox_restart_dialog_checkbox"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:button="@drawable/letterbox_restart_checkbox_button"/>
+
+                    <TextView
+                        android:textAppearance="@style/RestartDialogCheckboxText"
+                        android:layout_marginStart="12dp"
+                        android:id="@+id/letterbox_restart_dialog_checkbox_description"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:text="@string/letterbox_restart_dialog_checkbox_title"
+                        android:textAlignment="textStart"/>
+
+                </LinearLayout>
+
+                <FrameLayout
+                    android:minHeight="@dimen/letterbox_restart_dialog_button_height"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content">
+
+                    <Button
+                        android:textAppearance="@style/RestartDialogDismissButton"
+                        android:id="@+id/letterbox_restart_dialog_dismiss_button"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:minWidth="@dimen/letterbox_restart_dialog_button_width"
+                        android:minHeight="@dimen/letterbox_restart_dialog_button_height"
+                        android:layout_gravity="start"
+                        android:background=
+                            "@drawable/letterbox_restart_dismiss_button_background_ripple"
+                        android:text="@string/letterbox_restart_cancel"
+                        android:contentDescription="@string/letterbox_restart_cancel"/>
+
+                    <Button
+                        android:textAppearance="@style/RestartDialogConfirmButton"
+                        android:id="@+id/letterbox_restart_dialog_restart_button"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:minWidth="@dimen/letterbox_restart_dialog_button_width"
+                        android:minHeight="@dimen/letterbox_restart_dialog_button_height"
+                        android:layout_gravity="end"
+                        android:background=
+                            "@drawable/letterbox_restart_button_background_ripple"
+                        android:text="@string/letterbox_restart_restart"
+                        android:contentDescription="@string/letterbox_restart_restart"/>
+
+                </FrameLayout>
+
+            </LinearLayout>
+
+        </ScrollView>
+
+    </FrameLayout>
+
+</com.android.wm.shell.compatui.RestartDialogLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 3ee20ea..a1da649 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -273,6 +273,39 @@
     <!-- The space between two actions in the letterbox education dialog -->
     <dimen name="letterbox_education_dialog_space_between_actions">24dp</dimen>
 
+    <!-- The margin between the dialog container and its parent. -->
+    <dimen name="letterbox_restart_dialog_margin">24dp</dimen>
+
+    <!-- The corner radius of the restart confirmation dialog. -->
+    <dimen name="letterbox_restart_dialog_corner_radius">28dp</dimen>
+
+    <!-- The fixed width of the dialog if there is enough space in the parent. -->
+    <dimen name="letterbox_restart_dialog_width">348dp</dimen>
+
+    <!-- The width of the top icon in the restart confirmation dialog. -->
+    <dimen name="letterbox_restart_dialog_title_icon_width">32dp</dimen>
+
+    <!-- The height of the top icon in the restart confirmation dialog. -->
+    <dimen name="letterbox_restart_dialog_title_icon_height">32dp</dimen>
+
+    <!-- The width of an icon in the restart confirmation dialog. -->
+    <dimen name="letterbox_restart_dialog_icon_width">40dp</dimen>
+
+    <!-- The height of an icon in the restart confirmation dialog. -->
+    <dimen name="letterbox_restart_dialog_icon_height">32dp</dimen>
+
+    <!-- The space between two actions in the restart confirmation dialog -->
+    <dimen name="letterbox_restart_dialog_space_between_actions">24dp</dimen>
+
+    <!-- The width of the buttons in the restart dialog -->
+    <dimen name="letterbox_restart_dialog_button_width">82dp</dimen>
+
+    <!-- The width of the buttons in the restart dialog -->
+    <dimen name="letterbox_restart_dialog_button_height">36dp</dimen>
+
+    <!-- The corner radius of the buttons in the restart dialog -->
+    <dimen name="letterbox_restart_dialog_button_radius">18dp</dimen>
+
     <!-- The width of the brand image on staring surface. -->
     <dimen name="starting_surface_brand_image_width">200dp</dimen>
 
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 9490ddc..3fd97ef 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -193,6 +193,23 @@
     <!-- Accessibility description of the letterbox education toast expand to dialog button. [CHAR LIMIT=NONE] -->
     <string name="letterbox_education_expand_button_description">Expand for more information.</string>
 
+    <!-- The title of the restart confirmation dialog. [CHAR LIMIT=NONE] -->
+    <string name="letterbox_restart_dialog_title">Restart for a better view?</string>
+
+    <!-- The description of the restart confirmation dialog. [CHAR LIMIT=NONE] -->
+    <string name="letterbox_restart_dialog_description">You can restart the app so it looks better on
+        your screen, but you may lose your progress or any unsaved changes
+    </string>
+
+    <!-- Button text for dismissing the restart confirmation dialog. [CHAR LIMIT=20] -->
+    <string name="letterbox_restart_cancel">Cancel</string>
+
+    <!-- Button text for dismissing the restart confirmation dialog. [CHAR LIMIT=20] -->
+    <string name="letterbox_restart_restart">Restart</string>
+
+    <!-- Checkbox text for asking to not show the restart confirmation dialog again. [CHAR LIMIT=NONE] -->
+    <string name="letterbox_restart_dialog_checkbox_title">Don\u2019t show again</string>
+
     <!-- Freeform window caption strings -->
     <!-- Accessibility text for the maximize window button [CHAR LIMIT=NONE] -->
     <string name="maximize_button_text">Maximize</string>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index a859721..e8f340c 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -63,4 +63,42 @@
         <item name="android:lineHeight">16sp</item>
         <item name="android:textColor">@color/tv_pip_edu_text</item>
     </style>
+
+    <style name="RestartDialogTitleText">
+        <item name="android:textSize">24sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:lineSpacingExtra">2sp</item>
+        <item name="android:textAppearance">
+            @*android:style/TextAppearance.DeviceDefault.Headline
+        </item>
+    </style>
+
+    <style name="RestartDialogBodyText">
+        <item name="android:textSize">14sp</item>
+        <item name="android:letterSpacing">0.02</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:lineSpacingExtra">2sp</item>
+        <item name="android:textAppearance">
+            @*android:style/TextAppearance.DeviceDefault.Body2
+        </item>
+    </style>
+
+    <style name="RestartDialogCheckboxText">
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:lineSpacingExtra">4sp</item>
+        <item name="android:textAppearance">@*android:style/TextAppearance.DeviceDefault</item>
+    </style>
+
+    <style name="RestartDialogDismissButton">
+        <item name="android:lineSpacingExtra">2sp</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="RestartDialogConfirmButton">
+        <item name="android:lineSpacingExtra">2sp</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
+    </style>
 </resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java
index 4f33a71..e326e39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java
@@ -16,11 +16,12 @@
 
 package com.android.wm.shell.compatui;
 
+import android.annotation.NonNull;
+import android.app.TaskInfo;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.provider.DeviceConfig;
 
-import androidx.annotation.NonNull;
-
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.annotations.ShellMainThread;
@@ -34,11 +35,23 @@
 @WMSingleton
 public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedListener {
 
-    static final String KEY_ENABLE_LETTERBOX_RESTART_DIALOG = "enable_letterbox_restart_dialog";
+    private static final String KEY_ENABLE_LETTERBOX_RESTART_DIALOG =
+            "enable_letterbox_restart_dialog";
 
-    static final String KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION =
+    private static final String KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION =
             "enable_letterbox_reachability_education";
 
+    /**
+     * The name of the {@link SharedPreferences} that holds which user has seen the Restart
+     * confirmation dialog.
+     */
+    private static final String DONT_SHOW_RESTART_DIALOG_PREF_NAME = "dont_show_restart_dialog";
+
+    /**
+     * The {@link SharedPreferences} instance for {@link #DONT_SHOW_RESTART_DIALOG_PREF_NAME}.
+     */
+    private final SharedPreferences mSharedPreferences;
+
     // Whether the extended restart dialog is enabled
     private boolean mIsRestartDialogEnabled;
 
@@ -70,6 +83,8 @@
                 false);
         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT, mainExecutor,
                 this);
+        mSharedPreferences = context.getSharedPreferences(DONT_SHOW_RESTART_DIALOG_PREF_NAME,
+                Context.MODE_PRIVATE);
     }
 
     /**
@@ -102,6 +117,20 @@
         mIsReachabilityEducationOverrideEnabled = enabled;
     }
 
+    boolean getDontShowRestartDialogAgain(TaskInfo taskInfo) {
+        final int userId = taskInfo.userId;
+        final String packageName = taskInfo.topActivity.getPackageName();
+        return mSharedPreferences.getBoolean(
+                getDontShowAgainRestartKey(userId, packageName), /* default= */ false);
+    }
+
+    void setDontShowRestartDialogAgain(TaskInfo taskInfo) {
+        final int userId = taskInfo.userId;
+        final String packageName = taskInfo.topActivity.getPackageName();
+        mSharedPreferences.edit().putBoolean(getDontShowAgainRestartKey(userId, packageName),
+                true).apply();
+    }
+
     @Override
     public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
         // TODO(b/263349751): Update flag and default value to true
@@ -116,4 +145,8 @@
                     KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION, false);
         }
     }
-}
+
+    private String getDontShowAgainRestartKey(int userId, String packageName) {
+        return packageName + "@" + userId;
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 6627de5..3b2db51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -24,6 +24,7 @@
 import android.hardware.display.DisplayManager;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.InsetsSourceControl;
@@ -49,6 +50,7 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -91,6 +93,18 @@
     private final SparseArray<CompatUIWindowManager> mActiveCompatLayouts = new SparseArray<>(0);
 
     /**
+     * {@link SparseArray} that maps task ids to {@link RestartDialogWindowManager} that are
+     * currently visible
+     */
+    private final SparseArray<RestartDialogWindowManager> mTaskIdToRestartDialogWindowManagerMap =
+            new SparseArray<>(0);
+
+    /**
+     * {@link Set} of task ids for which we need to display a restart confirmation dialog
+     */
+    private Set<Integer> mSetOfTaskIdsShowingRestartDialog = new HashSet<>();
+
+    /**
      * The active Letterbox Education layout if there is one (there can be at most one active).
      *
      * <p>An active layout is a layout that is eligible to be shown for the associated task but
@@ -111,12 +125,15 @@
     private final ShellExecutor mMainExecutor;
     private final Lazy<Transitions> mTransitionsLazy;
     private final DockStateReader mDockStateReader;
+    private final CompatUIConfiguration mCompatUIConfiguration;
 
     private CompatUICallback mCallback;
 
     // Only show each hint once automatically in the process life.
     private final CompatUIHintsState mCompatUIHintsState;
 
+    private final CompatUIShellCommandHandler mCompatUIShellCommandHandler;
+
     // Indicates if the keyguard is currently showing, in which case compat UIs shouldn't
     // be shown.
     private boolean mKeyguardShowing;
@@ -130,7 +147,9 @@
             SyncTransactionQueue syncQueue,
             ShellExecutor mainExecutor,
             Lazy<Transitions> transitionsLazy,
-            DockStateReader dockStateReader) {
+            DockStateReader dockStateReader,
+            CompatUIConfiguration compatUIConfiguration,
+            CompatUIShellCommandHandler compatUIShellCommandHandler) {
         mContext = context;
         mShellController = shellController;
         mDisplayController = displayController;
@@ -140,14 +159,17 @@
         mMainExecutor = mainExecutor;
         mTransitionsLazy = transitionsLazy;
         mCompatUIHintsState = new CompatUIHintsState();
-        shellInit.addInitCallback(this::onInit, this);
         mDockStateReader = dockStateReader;
+        mCompatUIConfiguration = compatUIConfiguration;
+        mCompatUIShellCommandHandler = compatUIShellCommandHandler;
+        shellInit.addInitCallback(this::onInit, this);
     }
 
     private void onInit() {
         mShellController.addKeyguardChangeListener(this);
         mDisplayController.addDisplayWindowListener(this);
         mImeController.addPositionProcessor(this);
+        mCompatUIShellCommandHandler.onInit();
     }
 
     /** Sets the callback for UI interactions. */
@@ -164,6 +186,9 @@
      */
     public void onCompatInfoChanged(TaskInfo taskInfo,
             @Nullable ShellTaskOrganizer.TaskListener taskListener) {
+        if (taskInfo != null && !taskInfo.topActivityInSizeCompat) {
+            mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
+        }
         if (taskInfo.configuration == null || taskListener == null) {
             // Null token means the current foreground activity is not in compatibility mode.
             removeLayouts(taskInfo.taskId);
@@ -172,6 +197,7 @@
 
         createOrUpdateCompatLayout(taskInfo, taskListener);
         createOrUpdateLetterboxEduLayout(taskInfo, taskListener);
+        createOrUpdateRestartDialogLayout(taskInfo, taskListener);
     }
 
     @Override
@@ -278,7 +304,21 @@
             ShellTaskOrganizer.TaskListener taskListener) {
         return new CompatUIWindowManager(context,
                 taskInfo, mSyncQueue, mCallback, taskListener,
-                mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState);
+                mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState,
+                mCompatUIConfiguration, this::onRestartButtonClicked);
+    }
+
+    private void onRestartButtonClicked(
+            Pair<TaskInfo, ShellTaskOrganizer.TaskListener> taskInfoState) {
+        if (mCompatUIConfiguration.isRestartDialogEnabled()
+                && !mCompatUIConfiguration.getDontShowRestartDialogAgain(
+                taskInfoState.first)) {
+            // We need to show the dialog
+            mSetOfTaskIdsShowingRestartDialog.add(taskInfoState.first.taskId);
+            onCompatInfoChanged(taskInfoState.first, taskInfoState.second);
+        } else {
+            mCallback.onSizeCompatRestartButtonClicked(taskInfoState.first.taskId);
+        }
     }
 
     private void createOrUpdateLetterboxEduLayout(TaskInfo taskInfo,
@@ -327,6 +367,60 @@
         mActiveLetterboxEduLayout = null;
     }
 
+    private void createOrUpdateRestartDialogLayout(TaskInfo taskInfo,
+            ShellTaskOrganizer.TaskListener taskListener) {
+        RestartDialogWindowManager layout =
+                mTaskIdToRestartDialogWindowManagerMap.get(taskInfo.taskId);
+        if (layout != null) {
+            // TODO(b/266262111) Handle theme change when taskListener changes
+            if (layout.getTaskListener() != taskListener) {
+                mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
+            }
+            layout.setRequestRestartDialog(
+                    mSetOfTaskIdsShowingRestartDialog.contains(taskInfo.taskId));
+            // UI already exists, update the UI layout.
+            if (!layout.updateCompatInfo(taskInfo, taskListener,
+                    showOnDisplay(layout.getDisplayId()))) {
+                // The layout is no longer eligible to be shown, remove from active layouts.
+                mTaskIdToRestartDialogWindowManagerMap.remove(taskInfo.taskId);
+            }
+            return;
+        }
+        // Create a new UI layout.
+        final Context context = getOrCreateDisplayContext(taskInfo.displayId);
+        if (context == null) {
+            return;
+        }
+        layout = createRestartDialogWindowManager(context, taskInfo, taskListener);
+        layout.setRequestRestartDialog(
+                mSetOfTaskIdsShowingRestartDialog.contains(taskInfo.taskId));
+        if (layout.createLayout(showOnDisplay(taskInfo.displayId))) {
+            // The new layout is eligible to be shown, add it the active layouts.
+            mTaskIdToRestartDialogWindowManagerMap.put(taskInfo.taskId, layout);
+        }
+    }
+
+    @VisibleForTesting
+    RestartDialogWindowManager createRestartDialogWindowManager(Context context, TaskInfo taskInfo,
+            ShellTaskOrganizer.TaskListener taskListener) {
+        return new RestartDialogWindowManager(context, taskInfo, mSyncQueue, taskListener,
+                mDisplayController.getDisplayLayout(taskInfo.displayId), mTransitionsLazy.get(),
+                this::onRestartDialogCallback, this::onRestartDialogDismissCallback,
+                mCompatUIConfiguration);
+    }
+
+    private void onRestartDialogCallback(
+            Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
+        mTaskIdToRestartDialogWindowManagerMap.remove(stateInfo.first.taskId);
+        mCallback.onSizeCompatRestartButtonClicked(stateInfo.first.taskId);
+    }
+
+    private void onRestartDialogDismissCallback(
+            Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
+        mSetOfTaskIdsShowingRestartDialog.remove(stateInfo.first.taskId);
+        onCompatInfoChanged(stateInfo.first, stateInfo.second);
+    }
+
     private void removeLayouts(int taskId) {
         final CompatUIWindowManager layout = mActiveCompatLayouts.get(taskId);
         if (layout != null) {
@@ -338,6 +432,14 @@
             mActiveLetterboxEduLayout.release();
             mActiveLetterboxEduLayout = null;
         }
+
+        final RestartDialogWindowManager restartLayout =
+                mTaskIdToRestartDialogWindowManagerMap.get(taskId);
+        if (restartLayout != null) {
+            restartLayout.release();
+            mTaskIdToRestartDialogWindowManagerMap.remove(taskId);
+            mSetOfTaskIdsShowingRestartDialog.remove(taskId);
+        }
     }
 
     private Context getOrCreateDisplayContext(int displayId) {
@@ -382,6 +484,14 @@
         if (mActiveLetterboxEduLayout != null && condition.test(mActiveLetterboxEduLayout)) {
             callback.accept(mActiveLetterboxEduLayout);
         }
+        for (int i = 0; i < mTaskIdToRestartDialogWindowManagerMap.size(); i++) {
+            final int taskId = mTaskIdToRestartDialogWindowManagerMap.keyAt(i);
+            final RestartDialogWindowManager layout =
+                    mTaskIdToRestartDialogWindowManagerMap.get(taskId);
+            if (layout != null && condition.test(layout)) {
+                callback.accept(layout);
+            }
+        }
     }
 
     /** An implementation of {@link OnInsetsChangedListener} for a given display id. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index bce3ec4..c14704d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -21,12 +21,14 @@
 import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
 import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.TaskInfo;
 import android.app.TaskInfo.CameraCompatControlState;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.Log;
+import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
 
@@ -38,6 +40,8 @@
 import com.android.wm.shell.compatui.CompatUIController.CompatUICallback;
 import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
 
+import java.util.function.Consumer;
+
 /**
  * Window manager for the Size Compat restart button and Camera Compat control.
  */
@@ -50,6 +54,13 @@
 
     private final CompatUICallback mCallback;
 
+    private final CompatUIConfiguration mCompatUIConfiguration;
+
+    private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
+
+    @NonNull
+    private TaskInfo mTaskInfo;
+
     // Remember the last reported states in case visibility changes due to keyguard or IME updates.
     @VisibleForTesting
     boolean mHasSizeCompat;
@@ -68,12 +79,16 @@
     CompatUIWindowManager(Context context, TaskInfo taskInfo,
             SyncTransactionQueue syncQueue, CompatUICallback callback,
             ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
-            CompatUIHintsState compatUIHintsState) {
+            CompatUIHintsState compatUIHintsState, CompatUIConfiguration compatUIConfiguration,
+            Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartButtonClicked) {
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
+        mTaskInfo = taskInfo;
         mCallback = callback;
         mHasSizeCompat = taskInfo.topActivityInSizeCompat;
         mCameraCompatControlState = taskInfo.cameraCompatControlState;
         mCompatUIHintsState = compatUIHintsState;
+        mCompatUIConfiguration = compatUIConfiguration;
+        mOnRestartButtonClicked = onRestartButtonClicked;
     }
 
     @Override
@@ -119,6 +134,7 @@
     @Override
     public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
             boolean canShow) {
+        mTaskInfo = taskInfo;
         final boolean prevHasSizeCompat = mHasSizeCompat;
         final int prevCameraCompatControlState = mCameraCompatControlState;
         mHasSizeCompat = taskInfo.topActivityInSizeCompat;
@@ -138,7 +154,7 @@
 
     /** Called when the restart button is clicked. */
     void onRestartButtonClicked() {
-        mCallback.onSizeCompatRestartButtonClicked(mTaskId);
+        mOnRestartButtonClicked.accept(Pair.create(mTaskInfo, getTaskListener()));
     }
 
     /** Called when the camera treatment button is clicked. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
index 2cc9f45..34e650a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
@@ -151,6 +151,7 @@
     @Override
     public void setConfiguration(Configuration configuration) {
         super.setConfiguration(configuration);
+        // TODO(b/266262111): Investigate loss of theme configuration when switching TaskListener
         mContext = mContext.createConfigurationContext(configuration);
     }
 
@@ -168,6 +169,10 @@
         return mLeash;
     }
 
+    protected ShellTaskOrganizer.TaskListener getTaskListener() {
+        return mTaskListener;
+    }
+
     /** Inits the z-order of the surface. */
     private void initSurface(SurfaceControl leash) {
         final int z = getZOrder();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java
new file mode 100644
index 0000000..c53e638
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java
@@ -0,0 +1,107 @@
+/*
+ * 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.wm.shell.compatui;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.wm.shell.R;
+
+import java.util.function.Consumer;
+
+/**
+ * Container for a SCM restart confirmation dialog and background dim.
+ */
+public class RestartDialogLayout extends ConstraintLayout implements DialogContainerSupplier {
+
+    private View mDialogContainer;
+    private TextView mDialogTitle;
+    private Drawable mBackgroundDim;
+
+    public RestartDialogLayout(Context context) {
+        this(context, null);
+    }
+
+    public RestartDialogLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public RestartDialogLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public RestartDialogLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public View getDialogContainerView() {
+        return mDialogContainer;
+    }
+
+    TextView getDialogTitle() {
+        return mDialogTitle;
+    }
+
+    @Override
+    public Drawable getBackgroundDimDrawable() {
+        return mBackgroundDim;
+    }
+
+    /**
+     * Register a callback for the dismiss button and background dim.
+     *
+     * @param callback The callback to register or null if all on click listeners should be removed.
+     */
+    void setDismissOnClickListener(@Nullable Runnable callback) {
+        final OnClickListener listener = callback == null ? null : view -> callback.run();
+        findViewById(R.id.letterbox_restart_dialog_dismiss_button).setOnClickListener(listener);
+    }
+
+    /**
+     * Register a callback for the restart button
+     *
+     * @param callback The callback to register or null if all on click listeners should be removed.
+     */
+    void setRestartOnClickListener(@Nullable Consumer<Boolean> callback) {
+        final CheckBox dontShowAgainCheckbox = findViewById(R.id.letterbox_restart_dialog_checkbox);
+        final OnClickListener listener = callback == null ? null : view -> callback.accept(
+                dontShowAgainCheckbox.isChecked());
+        findViewById(R.id.letterbox_restart_dialog_restart_button).setOnClickListener(listener);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mDialogContainer = findViewById(R.id.letterbox_restart_dialog_container);
+        mDialogTitle = findViewById(R.id.letterbox_restart_dialog_title);
+        mBackgroundDim = getBackground().mutate();
+        // Set the alpha of the background dim to 0 for enter animation.
+        mBackgroundDim.setAlpha(0);
+        // We add a no-op on-click listener to the dialog container so that clicks on it won't
+        // propagate to the listener of the layout (which represents the background dim).
+        mDialogContainer.setOnClickListener(view -> {});
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java
new file mode 100644
index 0000000..10f25d0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java
@@ -0,0 +1,259 @@
+/*
+ * 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.wm.shell.compatui;
+
+import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.graphics.Rect;
+import android.provider.Settings;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.function.Consumer;
+
+/**
+ * Window manager for the Restart Dialog.
+ *
+ * TODO(b/263484314): Create abstraction of RestartDialogWindowManager and LetterboxEduWindowManager
+ */
+class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
+
+    /**
+     * The restart dialog should be the topmost child of the Task in case there can be more
+     * than one child.
+     */
+    private static final int Z_ORDER = Integer.MAX_VALUE;
+
+    private final DialogAnimationController<RestartDialogLayout> mAnimationController;
+
+    private final Transitions mTransitions;
+
+    // Remember the last reported state in case visibility changes due to keyguard or IME updates.
+    private boolean mRequestRestartDialog;
+
+    private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnDismissCallback;
+
+    private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartCallback;
+
+    private final CompatUIConfiguration mCompatUIConfiguration;
+
+    /**
+     * The vertical margin between the dialog container and the task stable bounds (excluding
+     * insets).
+     */
+    private final int mDialogVerticalMargin;
+
+    @NonNull
+    private TaskInfo mTaskInfo;
+
+    @Nullable
+    @VisibleForTesting
+    RestartDialogLayout mLayout;
+
+    RestartDialogWindowManager(Context context, TaskInfo taskInfo,
+            SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
+            DisplayLayout displayLayout, Transitions transitions,
+            Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartCallback,
+            Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onDismissCallback,
+            CompatUIConfiguration compatUIConfiguration) {
+        this(context, taskInfo, syncQueue, taskListener, displayLayout, transitions,
+                onRestartCallback, onDismissCallback,
+                new DialogAnimationController<>(context, "RestartDialogWindowManager"),
+                compatUIConfiguration);
+    }
+
+    @VisibleForTesting
+    RestartDialogWindowManager(Context context, TaskInfo taskInfo,
+            SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
+            DisplayLayout displayLayout, Transitions transitions,
+            Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartCallback,
+            Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onDismissCallback,
+            DialogAnimationController<RestartDialogLayout> animationController,
+            CompatUIConfiguration compatUIConfiguration) {
+        super(context, taskInfo, syncQueue, taskListener, displayLayout);
+        mTaskInfo = taskInfo;
+        mTransitions = transitions;
+        mOnDismissCallback = onDismissCallback;
+        mOnRestartCallback = onRestartCallback;
+        mAnimationController = animationController;
+        mDialogVerticalMargin = (int) mContext.getResources().getDimension(
+                R.dimen.letterbox_restart_dialog_margin);
+        mCompatUIConfiguration = compatUIConfiguration;
+    }
+
+    @Override
+    protected int getZOrder() {
+        return Z_ORDER;
+    }
+
+    @Override
+    @Nullable
+    protected  View getLayout() {
+        return mLayout;
+    }
+
+    @Override
+    protected void removeLayout() {
+        mLayout = null;
+    }
+
+    @Override
+    protected boolean eligibleToShowLayout() {
+        // We don't show this dialog if the user has explicitly selected so clicking on a checkbox.
+        return mRequestRestartDialog && !isTaskbarEduShowing() && (mLayout != null
+                || !mCompatUIConfiguration.getDontShowRestartDialogAgain(mTaskInfo));
+    }
+
+    @Override
+    protected View createLayout() {
+        mLayout = inflateLayout();
+        updateDialogMargins();
+
+        // startEnterAnimation will be called immediately if shell-transitions are disabled.
+        mTransitions.runOnIdle(this::startEnterAnimation);
+
+        return mLayout;
+    }
+
+    void setRequestRestartDialog(boolean enabled) {
+        mRequestRestartDialog = enabled;
+    }
+
+    @Override
+    public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
+            boolean canShow) {
+        mTaskInfo = taskInfo;
+        return super.updateCompatInfo(taskInfo, taskListener, canShow);
+    }
+
+    private void updateDialogMargins() {
+        if (mLayout == null) {
+            return;
+        }
+        final View dialogContainer = mLayout.getDialogContainerView();
+        ViewGroup.MarginLayoutParams marginParams =
+                (ViewGroup.MarginLayoutParams) dialogContainer.getLayoutParams();
+
+        final Rect taskBounds = getTaskBounds();
+        final Rect taskStableBounds = getTaskStableBounds();
+
+        marginParams.topMargin = taskStableBounds.top - taskBounds.top + mDialogVerticalMargin;
+        marginParams.bottomMargin =
+                taskBounds.bottom - taskStableBounds.bottom + mDialogVerticalMargin;
+        dialogContainer.setLayoutParams(marginParams);
+    }
+
+    private RestartDialogLayout inflateLayout() {
+        return (RestartDialogLayout) LayoutInflater.from(mContext).inflate(
+                R.layout.letterbox_restart_dialog_layout, null);
+    }
+
+    private void startEnterAnimation() {
+        if (mLayout == null) {
+            // Dialog has already been released.
+            return;
+        }
+        mAnimationController.startEnterAnimation(mLayout, /* endCallback= */
+                this::onDialogEnterAnimationEnded);
+    }
+
+    private void onDialogEnterAnimationEnded() {
+        if (mLayout == null) {
+            // Dialog has already been released.
+            return;
+        }
+        mLayout.setDismissOnClickListener(this::onDismiss);
+        mLayout.setRestartOnClickListener(dontShowAgain -> {
+            if (mLayout != null) {
+                mLayout.setDismissOnClickListener(null);
+                mAnimationController.startExitAnimation(mLayout, () -> {
+                    release();
+                });
+            }
+            if (dontShowAgain) {
+                mCompatUIConfiguration.setDontShowRestartDialogAgain(mTaskInfo);
+            }
+            mOnRestartCallback.accept(Pair.create(mTaskInfo, getTaskListener()));
+        });
+        // Focus on the dialog title for accessibility.
+        mLayout.getDialogTitle().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+    }
+
+    private void onDismiss() {
+        if (mLayout == null) {
+            return;
+        }
+
+        mLayout.setDismissOnClickListener(null);
+        mAnimationController.startExitAnimation(mLayout, () -> {
+            release();
+            mOnDismissCallback.accept(Pair.create(mTaskInfo, getTaskListener()));
+        });
+    }
+
+    @Override
+    public void release() {
+        mAnimationController.cancelAnimation();
+        super.release();
+    }
+
+    @Override
+    protected void onParentBoundsChanged() {
+        if (mLayout == null) {
+            return;
+        }
+        // Both the layout dimensions and dialog margins depend on the parent bounds.
+        WindowManager.LayoutParams windowLayoutParams = getWindowLayoutParams();
+        mLayout.setLayoutParams(windowLayoutParams);
+        updateDialogMargins();
+        relayout(windowLayoutParams);
+    }
+
+    @Override
+    protected void updateSurfacePosition() {
+        // Nothing to do, since the position of the surface is fixed to the top left corner (0,0)
+        // of the task (parent surface), which is the default position of a surface.
+    }
+
+    @Override
+    protected WindowManager.LayoutParams getWindowLayoutParams() {
+        final Rect taskBounds = getTaskBounds();
+        return getWindowLayoutParams(/* width= */ taskBounds.width(), /* height= */
+                taskBounds.height());
+    }
+
+    @VisibleForTesting
+    boolean isTaskbarEduShowing() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                LAUNCHER_TASKBAR_EDUCATION_SHOWING, /* def= */ 0) == 1;
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 7055aca0..eee8ad8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -57,7 +57,9 @@
 import com.android.wm.shell.common.annotations.ShellBackgroundThread;
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.compatui.CompatUIConfiguration;
 import com.android.wm.shell.compatui.CompatUIController;
+import com.android.wm.shell.compatui.CompatUIShellCommandHandler;
 import com.android.wm.shell.desktopmode.DesktopMode;
 import com.android.wm.shell.desktopmode.DesktopModeController;
 import com.android.wm.shell.desktopmode.DesktopModeStatus;
@@ -197,10 +199,11 @@
             DisplayController displayController, DisplayInsetsController displayInsetsController,
             DisplayImeController imeController, SyncTransactionQueue syncQueue,
             @ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy,
-            DockStateReader dockStateReader) {
+            DockStateReader dockStateReader, CompatUIConfiguration compatUIConfiguration,
+            CompatUIShellCommandHandler compatUIShellCommandHandler) {
         return new CompatUIController(context, shellInit, shellController, displayController,
                 displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy,
-                dockStateReader);
+                dockStateReader, compatUIConfiguration, compatUIShellCommandHandler);
     }
 
     @WMSingleton
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/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index d4408d7..05fd889 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -95,7 +95,10 @@
     private @Mock Lazy<Transitions> mMockTransitionsLazy;
     private @Mock CompatUIWindowManager mMockCompatLayout;
     private @Mock LetterboxEduWindowManager mMockLetterboxEduLayout;
+    private @Mock RestartDialogWindowManager mMockRestartDialogLayout;
     private @Mock DockStateReader mDockStateReader;
+    private @Mock CompatUIConfiguration mCompatUIConfiguration;
+    private @Mock CompatUIShellCommandHandler mCompatUIShellCommandHandler;
 
     @Captor
     ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -113,10 +116,17 @@
         doReturn(TASK_ID).when(mMockLetterboxEduLayout).getTaskId();
         doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
         doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
+
+        doReturn(DISPLAY_ID).when(mMockRestartDialogLayout).getDisplayId();
+        doReturn(TASK_ID).when(mMockRestartDialogLayout).getTaskId();
+        doReturn(true).when(mMockRestartDialogLayout).createLayout(anyBoolean());
+        doReturn(true).when(mMockRestartDialogLayout).updateCompatInfo(any(), any(), anyBoolean());
+
         mShellInit = spy(new ShellInit(mMockExecutor));
         mController = new CompatUIController(mContext, mShellInit, mMockShellController,
                 mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
-                mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader) {
+                mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader,
+                mCompatUIConfiguration, mCompatUIShellCommandHandler) {
             @Override
             CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
                     ShellTaskOrganizer.TaskListener taskListener) {
@@ -128,6 +138,12 @@
                     TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
                 return mMockLetterboxEduLayout;
             }
+
+            @Override
+            RestartDialogWindowManager createRestartDialogWindowManager(Context context,
+                    TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+                return mMockRestartDialogLayout;
+            }
         };
         mShellInit.init();
         spyOn(mController);
@@ -160,6 +176,8 @@
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
         verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
                 eq(mMockTaskListener));
+        verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
 
         // Verify that the compat controls and letterbox education are updated with new size compat
         // info.
@@ -168,10 +186,12 @@
                 CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                true);
-        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                true);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ true);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ true);
+        verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ true);
 
         // Verify that compat controls and letterbox education are removed with null task listener.
         clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
@@ -181,12 +201,14 @@
 
         verify(mMockCompatLayout).release();
         verify(mMockLetterboxEduLayout).release();
+        verify(mMockRestartDialogLayout).release();
     }
 
     @Test
     public void testOnCompatInfoChanged_createLayoutReturnsFalse() {
         doReturn(false).when(mMockCompatLayout).createLayout(anyBoolean());
         doReturn(false).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
+        doReturn(false).when(mMockRestartDialogLayout).createLayout(anyBoolean());
 
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
@@ -195,6 +217,8 @@
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
         verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
                 eq(mMockTaskListener));
+        verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
 
         // Verify that the layout is created again.
         clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
@@ -202,15 +226,19 @@
 
         verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
         verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+        verify(mMockRestartDialogLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
         verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
                 eq(mMockTaskListener));
+        verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
     }
 
     @Test
     public void testOnCompatInfoChanged_updateCompatInfoReturnsFalse() {
         doReturn(false).when(mMockCompatLayout).updateCompatInfo(any(), any(), anyBoolean());
         doReturn(false).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
+        doReturn(false).when(mMockRestartDialogLayout).updateCompatInfo(any(), any(), anyBoolean());
 
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
@@ -219,24 +247,33 @@
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
         verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
                 eq(mMockTaskListener));
+        verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
 
-        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
+        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout,
+                mController);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                true);
-        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                true);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ true);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ true);
+        verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ true);
 
         // Verify that the layout is created again.
-        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
+        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout,
+                mController);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
         verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
         verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+        verify(mMockRestartDialogLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
         verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
                 eq(mMockTaskListener));
+        verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
     }
 
 
@@ -260,6 +297,7 @@
 
         verify(mMockCompatLayout, never()).release();
         verify(mMockLetterboxEduLayout, never()).release();
+        verify(mMockRestartDialogLayout, never()).release();
         verify(mMockDisplayInsetsController, never()).removeInsetsChangedListener(eq(DISPLAY_ID),
                 any());
 
@@ -268,6 +306,7 @@
         verify(mMockDisplayInsetsController).removeInsetsChangedListener(eq(DISPLAY_ID), any());
         verify(mMockCompatLayout).release();
         verify(mMockLetterboxEduLayout).release();
+        verify(mMockRestartDialogLayout).release();
     }
 
     @Test
@@ -279,11 +318,13 @@
 
         verify(mMockCompatLayout, never()).updateDisplayLayout(any());
         verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(any());
+        verify(mMockRestartDialogLayout, never()).updateDisplayLayout(any());
 
         mController.onDisplayConfigurationChanged(DISPLAY_ID, new Configuration());
 
         verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
         verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockRestartDialogLayout).updateDisplayLayout(mMockDisplayLayout);
     }
 
     @Test
@@ -302,12 +343,14 @@
 
         verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
         verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockRestartDialogLayout).updateDisplayLayout(mMockDisplayLayout);
 
         // No update if the insets state is the same.
-        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
+        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout);
         mOnInsetsChangedListenerCaptor.getValue().insetsChanged(new InsetsState(insetsState));
         verify(mMockCompatLayout, never()).updateDisplayLayout(mMockDisplayLayout);
         verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockRestartDialogLayout, never()).updateDisplayLayout(mMockDisplayLayout);
     }
 
     @Test
@@ -320,22 +363,26 @@
 
         verify(mMockCompatLayout).updateVisibility(false);
         verify(mMockLetterboxEduLayout).updateVisibility(false);
+        verify(mMockRestartDialogLayout).updateVisibility(false);
 
         // Verify button remains hidden while IME is showing.
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                false);
-        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                false);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ false);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ false);
+        verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ false);
 
         // Verify button is shown after IME is hidden.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
 
         verify(mMockCompatLayout).updateVisibility(true);
         verify(mMockLetterboxEduLayout).updateVisibility(true);
+        verify(mMockRestartDialogLayout).updateVisibility(true);
     }
 
     @Test
@@ -348,22 +395,26 @@
 
         verify(mMockCompatLayout).updateVisibility(false);
         verify(mMockLetterboxEduLayout).updateVisibility(false);
+        verify(mMockRestartDialogLayout).updateVisibility(false);
 
         // Verify button remains hidden while keyguard is showing.
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                false);
-        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
-                false);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ false);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ false);
+        verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
+                /* canShow= */ false);
 
         // Verify button is shown after keyguard becomes not showing.
         mController.onKeyguardVisibilityChanged(false, false, false);
 
         verify(mMockCompatLayout).updateVisibility(true);
         verify(mMockLetterboxEduLayout).updateVisibility(true);
+        verify(mMockRestartDialogLayout).updateVisibility(true);
     }
 
     @Test
@@ -376,20 +427,23 @@
 
         verify(mMockCompatLayout, times(2)).updateVisibility(false);
         verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
+        verify(mMockRestartDialogLayout, times(2)).updateVisibility(false);
 
-        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
+        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout);
 
         // Verify button remains hidden after keyguard becomes not showing since IME is showing.
         mController.onKeyguardVisibilityChanged(false, false, false);
 
         verify(mMockCompatLayout).updateVisibility(false);
         verify(mMockLetterboxEduLayout).updateVisibility(false);
+        verify(mMockRestartDialogLayout).updateVisibility(false);
 
         // Verify button is shown after IME is not showing.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
 
         verify(mMockCompatLayout).updateVisibility(true);
         verify(mMockLetterboxEduLayout).updateVisibility(true);
+        verify(mMockRestartDialogLayout).updateVisibility(true);
     }
 
     @Test
@@ -402,20 +456,23 @@
 
         verify(mMockCompatLayout, times(2)).updateVisibility(false);
         verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
+        verify(mMockRestartDialogLayout, times(2)).updateVisibility(false);
 
-        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
+        clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout);
 
         // Verify button remains hidden after IME is hidden since keyguard is showing.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
 
         verify(mMockCompatLayout).updateVisibility(false);
         verify(mMockLetterboxEduLayout).updateVisibility(false);
+        verify(mMockRestartDialogLayout).updateVisibility(false);
 
         // Verify button is shown after keyguard becomes not showing.
         mController.onKeyguardVisibilityChanged(false, false, false);
 
         verify(mMockCompatLayout).updateVisibility(true);
         verify(mMockLetterboxEduLayout).updateVisibility(true);
+        verify(mMockRestartDialogLayout).updateVisibility(true);
     }
 
     private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index 7d3e718..5f294d5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -31,6 +31,7 @@
 import android.app.TaskInfo;
 import android.app.TaskInfo.CameraCompatControlState;
 import android.testing.AndroidTestingRunner;
+import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.SurfaceControlViewHost;
 import android.widget.ImageButton;
@@ -45,12 +46,17 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
 
+import junit.framework.Assert;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.function.Consumer;
+
 /**
  * Tests for {@link CompatUILayout}.
  *
@@ -65,20 +71,22 @@
 
     @Mock private SyncTransactionQueue mSyncTransactionQueue;
     @Mock private CompatUIController.CompatUICallback mCallback;
+    @Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
     @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
     @Mock private SurfaceControlViewHost mViewHost;
+    @Mock private CompatUIConfiguration mCompatUIConfiguration;
 
     private CompatUIWindowManager mWindowManager;
     private CompatUILayout mLayout;
+    private TaskInfo mTaskInfo;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-
-        mWindowManager = new CompatUIWindowManager(mContext,
-                createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
-                mSyncTransactionQueue, mCallback, mTaskListener,
-                new DisplayLayout(), new CompatUIHintsState());
+        mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
+        mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
+                mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
+                mCompatUIConfiguration, mOnRestartButtonClicked);
 
         mLayout = (CompatUILayout)
                 LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
@@ -95,8 +103,15 @@
         final ImageButton button = mLayout.findViewById(R.id.size_compat_restart_button);
         button.performClick();
 
+        @SuppressWarnings("unchecked")
+        ArgumentCaptor<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> restartCaptor =
+                ArgumentCaptor.forClass(Pair.class);
+
         verify(mWindowManager).onRestartButtonClicked();
-        verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+        verify(mOnRestartButtonClicked).accept(restartCaptor.capture());
+        final Pair<TaskInfo, ShellTaskOrganizer.TaskListener> result = restartCaptor.getValue();
+        Assert.assertEquals(mTaskInfo, result.first);
+        Assert.assertEquals(mTaskListener, result.second);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index c4d78bb..f24a7f2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -39,6 +39,7 @@
 import android.app.TaskInfo;
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
+import android.util.Pair;
 import android.view.DisplayInfo;
 import android.view.InsetsSource;
 import android.view.InsetsState;
@@ -54,12 +55,17 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
 
+import junit.framework.Assert;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.function.Consumer;
+
 /**
  * Tests for {@link CompatUIWindowManager}.
  *
@@ -74,20 +80,22 @@
 
     @Mock private SyncTransactionQueue mSyncTransactionQueue;
     @Mock private CompatUIController.CompatUICallback mCallback;
+    @Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
     @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
     @Mock private CompatUILayout mLayout;
     @Mock private SurfaceControlViewHost mViewHost;
+    @Mock private CompatUIConfiguration mCompatUIConfiguration;
 
     private CompatUIWindowManager mWindowManager;
+    private TaskInfo mTaskInfo;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-
-        mWindowManager = new CompatUIWindowManager(mContext,
-                createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
-                mSyncTransactionQueue, mCallback, mTaskListener,
-                new DisplayLayout(), new CompatUIHintsState());
+        mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
+        mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
+                mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
+                mCompatUIConfiguration, mOnRestartButtonClicked);
 
         spyOn(mWindowManager);
         doReturn(mLayout).when(mWindowManager).inflateLayout();
@@ -405,7 +413,14 @@
     public void testOnRestartButtonClicked() {
         mWindowManager.onRestartButtonClicked();
 
-        verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+        @SuppressWarnings("unchecked")
+        ArgumentCaptor<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> restartCaptor =
+                ArgumentCaptor.forClass(Pair.class);
+
+        verify(mOnRestartButtonClicked).accept(restartCaptor.capture());
+        final Pair<TaskInfo, ShellTaskOrganizer.TaskListener> result = restartCaptor.getValue();
+        Assert.assertEquals(mTaskInfo, result.first);
+        Assert.assertEquals(mTaskListener, result.second);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java
new file mode 100644
index 0000000..e2dcdb0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/RestartDialogLayoutTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.wm.shell.compatui;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Consumer;
+
+/**
+ * Tests for {@link RestartDialogLayout}.
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:RestartDialogLayoutTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class RestartDialogLayoutTest extends ShellTestCase  {
+
+    @Mock private Runnable mDismissCallback;
+    @Mock private Consumer<Boolean> mRestartCallback;
+
+    private RestartDialogLayout mLayout;
+    private View mDismissButton;
+    private View mRestartButton;
+    private View mDialogContainer;
+    private CheckBox mDontRepeatCheckBox;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mLayout = (RestartDialogLayout)
+                LayoutInflater.from(mContext).inflate(R.layout.letterbox_restart_dialog_layout,
+                        null);
+        mDismissButton = mLayout.findViewById(R.id.letterbox_restart_dialog_dismiss_button);
+        mRestartButton = mLayout.findViewById(R.id.letterbox_restart_dialog_restart_button);
+        mDialogContainer = mLayout.findViewById(R.id.letterbox_restart_dialog_container);
+        mDontRepeatCheckBox = mLayout.findViewById(R.id.letterbox_restart_dialog_checkbox);
+        mLayout.setDismissOnClickListener(mDismissCallback);
+        mLayout.setRestartOnClickListener(mRestartCallback);
+    }
+
+    @Test
+    public void testOnFinishInflate() {
+        assertEquals(mLayout.getDialogContainerView(),
+                mLayout.findViewById(R.id.letterbox_restart_dialog_container));
+        assertEquals(mLayout.getDialogTitle(),
+                mLayout.findViewById(R.id.letterbox_restart_dialog_title));
+        assertEquals(mLayout.getBackgroundDimDrawable(), mLayout.getBackground());
+        assertEquals(mLayout.getBackground().getAlpha(), 0);
+    }
+
+    @Test
+    public void testOnDismissButtonClicked() {
+        assertTrue(mDismissButton.performClick());
+
+        verify(mDismissCallback).run();
+    }
+
+    @Test
+    public void testOnRestartButtonClickedWithoutCheckbox() {
+        mDontRepeatCheckBox.setChecked(false);
+        assertTrue(mRestartButton.performClick());
+
+        verify(mRestartCallback).accept(false);
+    }
+
+    @Test
+    public void testOnRestartButtonClickedWithCheckbox() {
+        mDontRepeatCheckBox.setChecked(true);
+        assertTrue(mRestartButton.performClick());
+
+        verify(mRestartCallback).accept(true);
+    }
+
+    @Test
+    public void testOnBackgroundClickedDoesntDismiss() {
+        assertFalse(mLayout.performClick());
+
+        verify(mDismissCallback, never()).run();
+    }
+
+    @Test
+    public void testOnDialogContainerClicked() {
+        assertTrue(mDialogContainer.performClick());
+
+        verify(mDismissCallback, never()).run();
+        verify(mRestartCallback, never()).accept(anyBoolean());
+    }
+
+    @Test
+    public void testSetDismissOnClickListenerNull() {
+        mLayout.setDismissOnClickListener(null);
+
+        assertFalse(mDismissButton.performClick());
+        assertFalse(mLayout.performClick());
+        assertTrue(mDialogContainer.performClick());
+
+        verify(mDismissCallback, never()).run();
+    }
+
+    @Test
+    public void testSetRestartOnClickListenerNull() {
+        mLayout.setRestartOnClickListener(null);
+
+        assertFalse(mRestartButton.performClick());
+        assertFalse(mLayout.performClick());
+        assertTrue(mDialogContainer.performClick());
+
+        verify(mRestartCallback, never()).accept(anyBoolean());
+    }
+
+}
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..6f549dc 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -194,6 +194,8 @@
     ATRACE_CALL();
 
     if (window) {
+        // Ensure the hint session is running here, away from any critical paths
+        mHintSessionWrapper.init();
         mNativeSurface = std::make_unique<ReliableSurface>(window);
         mNativeSurface->init();
         if (enableTimeout) {
@@ -1021,6 +1023,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..8c9f65f 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -95,17 +95,13 @@
     }
 }
 
-bool HintSessionWrapper::useHintSession() {
-    if (!Properties::useHintManager || !Properties::isDrawingEnabled()) return false;
-    if (mHintSession) return true;
-    // If session does not exist, create it;
-    // this defers session creation until we try to actually use it.
-    if (!mSessionValid) return false;
-    return init();
-}
-
 bool HintSessionWrapper::init() {
-    if (mUiThreadId < 0 || mRenderThreadId < 0) return false;
+    // If it already exists, broke last time we tried this, shouldn't be running, or
+    // has bad argument values, don't even bother
+    if (mHintSession != nullptr || !mSessionValid || !Properties::useHintManager ||
+        !Properties::isDrawingEnabled() || mUiThreadId < 0 || mRenderThreadId < 0) {
+        return false;
+    }
 
     // Assume that if we return before the end, it broke
     mSessionValid = false;
@@ -130,7 +126,7 @@
 }
 
 void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) {
-    if (!useHintSession()) return;
+    if (mHintSession == nullptr) return;
     targetWorkDurationNanos = targetWorkDurationNanos * Properties::targetCpuTimePercentage / 100;
     if (targetWorkDurationNanos != mLastTargetWorkDuration &&
         targetWorkDurationNanos > kSanityCheckLowerBound &&
@@ -142,7 +138,7 @@
 }
 
 void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
-    if (!useHintSession()) return;
+    if (mHintSession == nullptr) return;
     if (actualDurationNanos > kSanityCheckLowerBound &&
         actualDurationNanos < kSanityCheckUpperBound) {
         gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
@@ -150,7 +146,7 @@
 }
 
 void HintSessionWrapper::sendLoadResetHint() {
-    if (!useHintSession()) return;
+    if (mHintSession == nullptr) return;
     nsecs_t now = systemTime();
     if (now - mLastFrameNotification > kResetHintTimeout) {
         gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET));
@@ -158,6 +154,11 @@
     mLastFrameNotification = now;
 }
 
+void HintSessionWrapper::sendLoadIncreaseHint() {
+    if (mHintSession == nullptr) 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..f2f1298 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -33,10 +33,10 @@
     void updateTargetWorkDuration(long targetDurationNanos);
     void reportActualWorkDuration(long actualDurationNanos);
     void sendLoadResetHint();
+    void sendLoadIncreaseHint();
+    bool init();
 
 private:
-    bool useHintSession();
-    bool init();
     APerformanceHintSession* mHintSession = nullptr;
 
     nsecs_t mLastFrameNotification = 0;
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/aidl/android/media/soundtrigger_middleware/OWNERS b/media/aidl/android/media/soundtrigger_middleware/OWNERS
index e5d0370..01b2cb9 100644
--- a/media/aidl/android/media/soundtrigger_middleware/OWNERS
+++ b/media/aidl/android/media/soundtrigger_middleware/OWNERS
@@ -1,2 +1,2 @@
-ytai@google.com
+atneya@google.com
 elaurent@google.com
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 813929e..7077a64 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1350,12 +1350,8 @@
     public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags) {
         Preconditions.checkNotNull(attr, "attr must not be null");
         final IAudioService service = getService();
-        try {
-            service.setVolumeIndexForAttributes(attr, index, flags,
-                    getContext().getOpPackageName(), getContext().getAttributionTag());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        int groupId = getVolumeGroupIdForAttributes(attr);
+        setVolumeGroupVolumeIndex(groupId, index, flags);
     }
 
     /**
@@ -1374,11 +1370,8 @@
     public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
         Preconditions.checkNotNull(attr, "attr must not be null");
         final IAudioService service = getService();
-        try {
-            return service.getVolumeIndexForAttributes(attr);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        int groupId = getVolumeGroupIdForAttributes(attr);
+        return getVolumeGroupVolumeIndex(groupId);
     }
 
     /**
@@ -1395,11 +1388,8 @@
     public int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
         Preconditions.checkNotNull(attr, "attr must not be null");
         final IAudioService service = getService();
-        try {
-            return service.getMaxVolumeIndexForAttributes(attr);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        int groupId = getVolumeGroupIdForAttributes(attr);
+        return getVolumeGroupMaxVolumeIndex(groupId);
     }
 
     /**
@@ -1416,8 +1406,182 @@
     public int getMinVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
         Preconditions.checkNotNull(attr, "attr must not be null");
         final IAudioService service = getService();
+        int groupId = getVolumeGroupIdForAttributes(attr);
+        return getVolumeGroupMinVolumeIndex(groupId);
+    }
+
+    /**
+     * Returns the volume group id associated to the given {@link AudioAttributes}.
+     *
+     * @param attributes The {@link AudioAttributes} to consider.
+     * @return {@link android.media.audiopolicy.AudioVolumeGroup} id supporting the given
+     * {@link AudioAttributes} if found,
+     * {@code android.media.audiopolicy.AudioVolumeGroup.DEFAULT_VOLUME_GROUP} otherwise.
+     */
+    public int getVolumeGroupIdForAttributes(@NonNull AudioAttributes attributes) {
+        Preconditions.checkNotNull(attributes, "Audio Attributes must not be null");
+        return AudioProductStrategy.getVolumeGroupIdForAudioAttributes(attributes,
+                /* fallbackOnDefault= */ false);
+    }
+
+    /**
+     * Sets the volume index for a particular group associated to given id.
+     * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)}
+     * to retrieve the volume group id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @param index The volume index to set. See
+     *          {@link #getVolumeGroupMaxVolumeIndex(id)} for the largest valid value
+     *          {@link #getVolumeGroupMinVolumeIndex(id)} for the lowest valid value.
+     * @param flags One or more flags.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING
+    })
+    public void setVolumeGroupVolumeIndex(int groupId, int index, int flags) {
+        final IAudioService service = getService();
         try {
-            return service.getMinVolumeIndexForAttributes(attr);
+            service.setVolumeGroupVolumeIndex(groupId, index, flags,
+                    getContext().getOpPackageName(), getContext().getAttributionTag());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current volume index for a particular group associated to given id.
+     * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)}
+     * to retrieve the volume group id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @return The current volume index for the stream.
+     * @hide
+     */
+    @SystemApi
+    @IntRange(from = 0)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING
+    })
+    public int getVolumeGroupVolumeIndex(int groupId) {
+        final IAudioService service = getService();
+        try {
+            return service.getVolumeGroupVolumeIndex(groupId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the maximum volume index for a particular group associated to given id.
+     * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)}
+     * to retrieve the volume group id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @return The maximum valid volume index for the {@link AudioAttributes}.
+     * @hide
+     */
+    @SystemApi
+    @IntRange(from = 0)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING
+    })
+    public int getVolumeGroupMaxVolumeIndex(int groupId) {
+        final IAudioService service = getService();
+        try {
+            return service.getVolumeGroupMaxVolumeIndex(groupId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the minimum volume index for a particular group associated to given id.
+     * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)}
+     * to retrieve the volume group id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @return The minimum valid volume index for the {@link AudioAttributes}.
+     * @hide
+     */
+    @SystemApi
+    @IntRange(from = 0)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING
+    })
+    public int getVolumeGroupMinVolumeIndex(int groupId) {
+        final IAudioService service = getService();
+        try {
+            return service.getVolumeGroupMinVolumeIndex(groupId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Adjusts the volume of a particular group associated to given id by one step in a direction.
+     * <p> If the volume group is associated to a stream type, it fallbacks on
+     * {@link #adjustStreamVolume(int, int, int)} for compatibility reason.
+     * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)} to retrieve
+     * the volume group id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @param direction The direction to adjust the volume. One of
+     *            {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
+     *            {@link #ADJUST_SAME}.
+     * @param flags One or more flags.
+     * @throws SecurityException if the adjustment triggers a Do Not Disturb change and the caller
+     * is not granted notification policy access.
+     */
+    public void adjustVolumeGroupVolume(int groupId, int direction, int flags) {
+        IAudioService service = getService();
+        try {
+            service.adjustVolumeGroupVolume(groupId, direction, flags,
+                    getContext().getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get last audible volume of the group associated to given id before it was muted.
+     * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)} to retrieve
+     * the volume group id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @return current volume if not muted, volume before muted otherwise.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
+    @IntRange(from = 0)
+    public int getLastAudibleVolumeGroupVolume(int groupId) {
+        IAudioService service = getService();
+        try {
+            return service.getLastAudibleVolumeGroupVolume(groupId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current mute state for a particular volume group associated to the given id.
+     * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)} to retrieve
+     * the volume group id supporting the given {@link AudioAttributes}.
+     *
+     * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+     * @return The mute state for the given {@link android.media.audiopolicy.AudioVolumeGroup} id.
+     * @see #adjustVolumeGroupVolume(int, int, int)
+     */
+    public boolean isVolumeGroupMuted(int groupId) {
+        IAudioService service = getService();
+        try {
+            return service.isVolumeGroupMuted(groupId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 5ba7891..ad3a44a 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -138,18 +138,25 @@
     @EnforcePermission("MODIFY_AUDIO_ROUTING")
     List<AudioVolumeGroup> getAudioVolumeGroups();
 
-    @EnforcePermission("MODIFY_AUDIO_ROUTING")
-    void setVolumeIndexForAttributes(in AudioAttributes aa, int index, int flags,
-            String callingPackage, in String attributionTag);
+    @EnforcePermission(anyOf={"MODIFY_AUDIO_SYSTEM_SETTINGS", "MODIFY_AUDIO_ROUTING"})
+    void setVolumeGroupVolumeIndex(int groupId, int index, int flags, String callingPackage,
+            in String attributionTag);
 
-    @EnforcePermission("MODIFY_AUDIO_ROUTING")
-    int getVolumeIndexForAttributes(in AudioAttributes aa);
+    @EnforcePermission(anyOf={"MODIFY_AUDIO_SYSTEM_SETTINGS", "MODIFY_AUDIO_ROUTING"})
+    int getVolumeGroupVolumeIndex(int groupId);
 
-    @EnforcePermission("MODIFY_AUDIO_ROUTING")
-    int getMaxVolumeIndexForAttributes(in AudioAttributes aa);
+    @EnforcePermission(anyOf={"MODIFY_AUDIO_SYSTEM_SETTINGS", "MODIFY_AUDIO_ROUTING"})
+    int getVolumeGroupMaxVolumeIndex(int groupId);
 
-    @EnforcePermission("MODIFY_AUDIO_ROUTING")
-    int getMinVolumeIndexForAttributes(in AudioAttributes aa);
+    @EnforcePermission(anyOf={"MODIFY_AUDIO_SYSTEM_SETTINGS", "MODIFY_AUDIO_ROUTING"})
+    int getVolumeGroupMinVolumeIndex(int groupId);
+
+    @EnforcePermission("QUERY_AUDIO_STATE")
+    int getLastAudibleVolumeGroupVolume(int groupId);
+
+    boolean isVolumeGroupMuted(int groupId);
+
+    void adjustVolumeGroupVolume(int groupId, int direction, int flags, String callingPackage);
 
     @EnforcePermission("QUERY_AUDIO_STATE")
     int getLastAudibleStreamVolume(int streamType);
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index aa7e4df..f739365 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -94,4 +94,5 @@
     void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId,
             String sessionId, int volume);
     void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String sessionId);
+    void showMediaOutputSwitcher(String packageName);
 }
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index a7959c5..24ec227 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -461,6 +461,19 @@
     }
 
     /**
+     * Shows the system UI output switcher.
+     */
+    public void showSystemOutputSwitcher() {
+        synchronized (mLock) {
+            try {
+                mMediaRouterService.showMediaOutputSwitcher(mPackageName);
+            } catch (RemoteException ex) {
+                ex.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * Sets the {@link RouteListingPreference} of the app associated to this media router.
      *
      * <p>Use this method to inform the system UI of the routes that you would like to list for
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index 0198419..f84eec6 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -233,6 +233,16 @@
 
     /**
      * @hide
+     * @return the product strategy ID (which is the generalisation of Car Audio Usage / legacy
+     *         routing_strategy linked to {@link AudioAttributes#getUsage()}).
+     */
+    @SystemApi
+    @NonNull public String getName() {
+        return mName;
+    }
+
+    /**
+     * @hide
      * @return first {@link AudioAttributes} associated to this product strategy.
      */
     @SystemApi
diff --git a/media/java/android/media/soundtrigger/OWNERS b/media/java/android/media/soundtrigger/OWNERS
index e5d0370..01b2cb9 100644
--- a/media/java/android/media/soundtrigger/OWNERS
+++ b/media/java/android/media/soundtrigger/OWNERS
@@ -1,2 +1,2 @@
-ytai@google.com
+atneya@google.com
 elaurent@google.com
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 1a41bc2..35ee3ee9 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -2827,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/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index cb2baa9..ae6f71c 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -143,6 +143,8 @@
     <!-- [CHAR LIMIT=100] -->
     <string name="uninstall_done_app">Uninstalled <xliff:g id="package_label">%1$s</xliff:g></string>
     <!-- [CHAR LIMIT=100] -->
+    <string name="uninstall_done_clone_app">Deleted <xliff:g id="package_label">%1$s</xliff:g> clone</string>
+    <!-- [CHAR LIMIT=100] -->
     <string name="uninstall_failed">Uninstall unsuccessful.</string>
     <!-- [CHAR LIMIT=100] -->
     <string name="uninstall_failed_app">Uninstalling <xliff:g id="package_label">%1$s</xliff:g> unsuccessful.</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
index b9552fc..e089aef 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
@@ -49,6 +49,7 @@
 
     static final String EXTRA_UNINSTALL_ID = "com.android.packageinstaller.extra.UNINSTALL_ID";
     static final String EXTRA_APP_LABEL = "com.android.packageinstaller.extra.APP_LABEL";
+    static final String EXTRA_IS_CLONE_APP = "com.android.packageinstaller.extra.IS_CLONE_APP";
 
     @Override
     public void onReceive(Context context, Intent intent) {
@@ -84,7 +85,10 @@
             case PackageInstaller.STATUS_SUCCESS:
                 notificationManager.cancel(uninstallId);
 
-                Toast.makeText(context, context.getString(R.string.uninstall_done_app, appLabel),
+                boolean isCloneApp = intent.getBooleanExtra(EXTRA_IS_CLONE_APP, false);
+                Toast.makeText(context, isCloneApp
+                                ? context.getString(R.string.uninstall_done_clone_app, appLabel)
+                                : context.getString(R.string.uninstall_done_app, appLabel),
                         Toast.LENGTH_LONG).show();
                 return;
             case PackageInstaller.STATUS_FAILURE_BLOCKED: {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 04496b9..7250bdd 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -67,6 +67,7 @@
     private static final String TAG = "UninstallerActivity";
 
     private static final String UNINSTALLING_CHANNEL = "uninstalling";
+    private boolean mIsClonedApp;
 
     public static class DialogInfo {
         public ApplicationInfo appInfo;
@@ -277,6 +278,14 @@
         fragment.show(ft, "dialog");
     }
 
+    /**
+     * Starts uninstall of app.
+     */
+    public void startUninstallProgress(boolean keepData, boolean isClonedApp) {
+        mIsClonedApp = isClonedApp;
+        startUninstallProgress(keepData);
+    }
+
     public void startUninstallProgress(boolean keepData) {
         boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
         CharSequence label = mDialogInfo.appInfo.loadSafeLabel(getPackageManager());
@@ -329,6 +338,7 @@
             broadcastIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
             broadcastIntent.putExtra(UninstallFinish.EXTRA_APP_LABEL, label);
             broadcastIntent.putExtra(UninstallFinish.EXTRA_UNINSTALL_ID, uninstallId);
+            broadcastIntent.putExtra(UninstallFinish.EXTRA_IS_CLONE_APP, mIsClonedApp);
 
             PendingIntent pendingIntent =
                     PendingIntent.getBroadcast(this, uninstallId, broadcastIntent,
@@ -343,7 +353,10 @@
             Notification uninstallingNotification =
                     (new Notification.Builder(this, UNINSTALLING_CHANNEL))
                     .setSmallIcon(R.drawable.ic_remove).setProgress(0, 1, true)
-                    .setContentTitle(getString(R.string.uninstalling_app, label)).setOngoing(true)
+                    .setContentTitle(mIsClonedApp
+                            ? getString(R.string.uninstalling_cloned_app, label)
+                            : getString(R.string.uninstalling_app, label))
+                            .setOngoing(true)
                     .build();
 
             notificationManager.notify(uninstallId, uninstallingNotification);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index 1bbdad5..4a93bf8 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -54,6 +54,7 @@
     private static final String LOG_TAG = UninstallAlertDialogFragment.class.getSimpleName();
 
     private @Nullable CheckBox mKeepData;
+    private boolean mIsClonedApp;
 
     /**
      * Get number of bytes of the app data of the package.
@@ -125,7 +126,6 @@
                 messageBuilder.append(" ").append(appLabel).append(".\n\n");
             }
         }
-        boolean isClonedApp = false;
 
         final boolean isUpdate =
                 ((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
@@ -154,7 +154,7 @@
                                     userName));
                 } else if (customUserManager.isUserOfType(USER_TYPE_PROFILE_CLONE)
                         && customUserManager.isSameProfileGroup(dialogInfo.user, myUserHandle)) {
-                    isClonedApp = true;
+                    mIsClonedApp = true;
                     messageBuilder.append(getString(
                             R.string.uninstall_application_text_current_user_clone_profile));
                 } else {
@@ -162,7 +162,7 @@
                             getString(R.string.uninstall_application_text_user, userName));
                 }
             } else if (isCloneProfile(myUserHandle)) {
-                isClonedApp = true;
+                mIsClonedApp = true;
                 messageBuilder.append(getString(
                         R.string.uninstall_application_text_current_user_clone_profile));
             } else {
@@ -177,7 +177,7 @@
             }
         }
 
-        if (isClonedApp) {
+        if (mIsClonedApp) {
             dialogBuilder.setTitle(getString(R.string.cloned_app_label, appLabel));
         } else {
             dialogBuilder.setTitle(appLabel);
@@ -236,7 +236,7 @@
         UserManager userManager = getContext().getSystemService(UserManager.class);
         List<UserHandle> profiles = userManager.getUserProfiles();
         for (UserHandle userHandle : profiles) {
-            if (!Process.myUserHandle().equals(UserHandle.SYSTEM) && isCloneProfile(userHandle)) {
+            if (!userHandle.equals(UserHandle.SYSTEM) && isCloneProfile(userHandle)) {
                 cloneUser = userHandle;
                 break;
             }
@@ -260,7 +260,7 @@
     public void onClick(DialogInterface dialog, int which) {
         if (which == Dialog.BUTTON_POSITIVE) {
             ((UninstallerActivity) getActivity()).startUninstallProgress(
-                    mKeepData != null && mKeepData.isChecked());
+                    mKeepData != null && mKeepData.isChecked(), mIsClonedApp);
         } else {
             ((UninstallerActivity) getActivity()).dispatchAborted();
         }
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-af/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-af/strings.xml
index b5a70d0..595e46d 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-af/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-af/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Toegelaat"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nie toegelaat nie"</string>
     <string name="version_text" msgid="4001669804596458577">"weergawe <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-kloon"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-am/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-am/strings.xml
index 3cc6e89..e2a1bf5 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-am/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-am/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"ይፈቀዳል"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"አይፈቀድም"</string>
     <string name="version_text" msgid="4001669804596458577">"ሥሪት <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"የተባዛ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ar/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ar/strings.xml
index 2e0a9ef..9a8e7dd 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ar/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ar/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"مسموح به"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"غير مسموح به"</string>
     <string name="version_text" msgid="4001669804596458577">"الإصدار <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"نسخة طبق الأصل من \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\""</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-as/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-as/strings.xml
index dd27aa1..2e8140e 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-as/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-as/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"অনুমতি দিয়া হৈছে"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"অনুমতি নাই"</string>
     <string name="version_text" msgid="4001669804596458577">"সংস্কৰণ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ৰ ক্ল’ন"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-az/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-az/strings.xml
index 1fc9c36..c6518c8 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-az/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-az/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"İcazə verildi"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"İcazə verilməyib"</string>
     <string name="version_text" msgid="4001669804596458577">"versiya <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kopyalanması"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-b+sr+Latn/strings.xml
index 3d5caaf..f874c6d 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-b+sr+Latn/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Dozvoljeno"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nije dozvoljeno"</string>
     <string name="version_text" msgid="4001669804596458577">"verzija <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-be/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-be/strings.xml
index f3c3dd0..e3d3926 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-be/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-be/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Дазволена"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Забаронена"</string>
     <string name="version_text" msgid="4001669804596458577">"версія <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Клон \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\""</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-bg/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-bg/strings.xml
index c16bea8..0f51436 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-bg/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-bg/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Разрешено"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Не е разрешено"</string>
     <string name="version_text" msgid="4001669804596458577">"версия <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Копие на <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-bn/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-bn/strings.xml
index a72570f..71b3239 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-bn/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-bn/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"অনুমোদিত"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"অননুমোদিত"</string>
     <string name="version_text" msgid="4001669804596458577">"ভার্সন <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ক্লোন"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-bs/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-bs/strings.xml
index 9b98057..35983f6 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-bs/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-bs/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Dozvoljeno"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nije dozvoljeno"</string>
     <string name="version_text" msgid="4001669804596458577">"verzija <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Kloniranje paketa <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ca/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ca/strings.xml
index 111abe3..70604be 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ca/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ca/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Amb permís"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Sense permís"</string>
     <string name="version_text" msgid="4001669804596458577">"versió <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-cs/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-cs/strings.xml
index f58e9c4..5a3e043 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-cs/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-cs/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Povoleno"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nepovoleno"</string>
     <string name="version_text" msgid="4001669804596458577">"verze <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon balíčku <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-da/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-da/strings.xml
index 32ae008..c53441c 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-da/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-da/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Tilladt"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Ikke tilladt"</string>
     <string name="version_text" msgid="4001669804596458577">"version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon af <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-de/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-de/strings.xml
index 401d8c9..b10f020 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-de/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-de/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Zulässig"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nicht zulässig"</string>
     <string name="version_text" msgid="4001669804596458577">"Version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-Klon"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-el/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-el/strings.xml
index ad751f5..ac4106a 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-el/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-el/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Επιτρέπεται"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Δεν επιτρέπεται"</string>
     <string name="version_text" msgid="4001669804596458577">"έκδοση <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Κλώνος <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-en-rAU/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-en-rAU/strings.xml
index b151c95..a0772f8 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-en-rAU/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Allowed"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Not allowed"</string>
     <string name="version_text" msgid="4001669804596458577">"Version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> clone"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-en-rGB/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-en-rGB/strings.xml
index b151c95..a0772f8 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-en-rGB/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Allowed"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Not allowed"</string>
     <string name="version_text" msgid="4001669804596458577">"Version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> clone"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-en-rIN/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-en-rIN/strings.xml
index b151c95..a0772f8 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-en-rIN/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Allowed"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Not allowed"</string>
     <string name="version_text" msgid="4001669804596458577">"Version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> clone"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-es-rUS/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-es-rUS/strings.xml
index b08a73f..08da5f8 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-es-rUS/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitida"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"No se permite"</string>
     <string name="version_text" msgid="4001669804596458577">"versión <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-es/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-es/strings.xml
index 45da42c..32ba2f9 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-es/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-es/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitida"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"No permitida"</string>
     <string name="version_text" msgid="4001669804596458577">"versión <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-et/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-et/strings.xml
index d8f43d8..a216abc 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-et/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-et/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Lubatud"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Pole lubatud"</string>
     <string name="version_text" msgid="4001669804596458577">"versioon <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Üksuse <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kloon"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-eu/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-eu/strings.xml
index 6505096..798cf35 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-eu/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-eu/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Baimena dauka"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Ez dauka baimenik"</string>
     <string name="version_text" msgid="4001669804596458577">"<xliff:g id="VERSION_NUM">%1$s</xliff:g> bertsioa"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> aplikazioaren klona"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-fa/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-fa/strings.xml
index 616cf87..8654c64 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-fa/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-fa/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"مجاز"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"غیرمجاز"</string>
     <string name="version_text" msgid="4001669804596458577">"نسخه <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"همتای <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-fi/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-fi/strings.xml
index 161f2ae..8f42d50 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-fi/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-fi/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Sallittu"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Ei sallittu"</string>
     <string name="version_text" msgid="4001669804596458577">"versio <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> klooni"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-fr-rCA/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-fr-rCA/strings.xml
index 5fd70cc..a271ff5 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-fr-rCA/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Autorisé"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Non autorisée"</string>
     <string name="version_text" msgid="4001669804596458577">"version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-fr/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-fr/strings.xml
index 239a86a..30f2bcd 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-fr/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-fr/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Autorisé"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Non autorisé"</string>
     <string name="version_text" msgid="4001669804596458577">"version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-gl/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-gl/strings.xml
index 809d24d..a98e809 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-gl/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-gl/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitida"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Non permitida"</string>
     <string name="version_text" msgid="4001669804596458577">"versión <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-gu/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-gu/strings.xml
index a1de2fb..7bff012 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-gu/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-gu/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"મંજૂરી છે"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"મંજૂરી નથી"</string>
     <string name="version_text" msgid="4001669804596458577">"વર્ઝન <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ની ક્લોન"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-hi/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-hi/strings.xml
index 9af4a02..1a962f5 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-hi/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-hi/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"अनुमति है"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"अनुमति नहीं है"</string>
     <string name="version_text" msgid="4001669804596458577">"वर्शन <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> का क्लोन"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-hr/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-hr/strings.xml
index 8556cfb..d179415 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-hr/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-hr/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Dopušteno"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nije dopušteno"</string>
     <string name="version_text" msgid="4001669804596458577">"verzija <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-hu/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-hu/strings.xml
index 9d928e9..4ccc59f 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-hu/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-hu/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Engedélyezve"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nem engedélyezett"</string>
     <string name="version_text" msgid="4001669804596458577">"verzió: <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> klónja"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-hy/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-hy/strings.xml
index 064d9998..67b3e73 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-hy/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-hy/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Թույլատրված է"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Արգելված է"</string>
     <string name="version_text" msgid="4001669804596458577">"տարբերակ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"«<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>» հավելվածի կլոն"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-in/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-in/strings.xml
index 8799c9b..a2a4289c 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-in/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-in/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Diizinkan"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Tidak diizinkan"</string>
     <string name="version_text" msgid="4001669804596458577">"versi <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-is/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-is/strings.xml
index ff7ee6a..5c21bec 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-is/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-is/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Leyft"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Ekki leyft"</string>
     <string name="version_text" msgid="4001669804596458577">"útgáfa <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Afrit af <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-it/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-it/strings.xml
index 5d67307..c8cbe76 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-it/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-it/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Consentita"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Non consentita"</string>
     <string name="version_text" msgid="4001669804596458577">"versione <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone di <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-iw/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-iw/strings.xml
index 4ab76fe..49b6b1f 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-iw/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-iw/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"יש הרשאה"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"אין הרשאה"</string>
     <string name="version_text" msgid="4001669804596458577">"גרסה <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"שכפול של <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ja/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ja/strings.xml
index 1c0d8d3..2f15ab4 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ja/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ja/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"許可"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"許可しない"</string>
     <string name="version_text" msgid="4001669804596458577">"バージョン <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> のクローン"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ka/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ka/strings.xml
index a4c6783..502fcb1 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ka/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ka/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"დაშვებულია"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"დაუშვებელია"</string>
     <string name="version_text" msgid="4001669804596458577">"<xliff:g id="VERSION_NUM">%1$s</xliff:g> ვერსია"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> კლონი"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-kk/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-kk/strings.xml
index 4760d47..1b5e8ed 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-kk/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-kk/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Рұқсат етілген"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Рұқсат етілмеген"</string>
     <string name="version_text" msgid="4001669804596458577">"<xliff:g id="VERSION_NUM">%1$s</xliff:g> нұсқасы"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> клоны"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-km/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-km/strings.xml
index 962fb5c..2eeb5ce 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-km/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-km/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"បាន​អនុញ្ញាត"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"មិន​អនុញ្ញាត​ទេ"</string>
     <string name="version_text" msgid="4001669804596458577">"កំណែ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"ក្លូន <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-kn/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-kn/strings.xml
index 7edec75..04e7396 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-kn/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-kn/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"ಅನುಮತಿಸಲಾಗಿದೆ"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ"</string>
     <string name="version_text" msgid="4001669804596458577">"ಆವೃತ್ತಿ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ಕ್ಲೋನ್"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ko/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ko/strings.xml
index 446580b..ef4ee0d 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ko/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ko/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"허용됨"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"허용되지 않음"</string>
     <string name="version_text" msgid="4001669804596458577">"버전 <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> 클론"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ky/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ky/strings.xml
index 2596b93..26fe636 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ky/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ky/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Уруксат берилген"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Тыюу салынган"</string>
     <string name="version_text" msgid="4001669804596458577">"<xliff:g id="VERSION_NUM">%1$s</xliff:g> версиясы"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> клону"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-lo/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-lo/strings.xml
index 0dc64a6..e707c9d 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-lo/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-lo/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"ອະນຸຍາດແລ້ວ"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ບໍ່ໄດ້ອະນຸຍາດ"</string>
     <string name="version_text" msgid="4001669804596458577">"ເວີຊັນ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"ໂຄລນ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-lt/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-lt/strings.xml
index 4ca01dc..8bcee5f 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-lt/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-lt/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Leidžiama"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Neleidžiama"</string>
     <string name="version_text" msgid="4001669804596458577">"<xliff:g id="VERSION_NUM">%1$s</xliff:g> versija"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"„<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>“ kopija"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-lv/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-lv/strings.xml
index bf300ac..6d5017c 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-lv/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-lv/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Atļauja piešķirta"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Atļauja nav piešķirta"</string>
     <string name="version_text" msgid="4001669804596458577">"versija <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Lietotnes <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> klons"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-mk/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-mk/strings.xml
index 839e4c7..56ed2d9 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-mk/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-mk/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Со дозволен пристап"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Без дозволен пристап"</string>
     <string name="version_text" msgid="4001669804596458577">"верзија <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Клон на <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ml/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ml/strings.xml
index 0aad964..1090690 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ml/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ml/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"അനുവാദം ലഭിച്ചു"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"അനുവാദം ലഭിച്ചില്ല"</string>
     <string name="version_text" msgid="4001669804596458577">"പതിപ്പ് <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ക്ലോൺ ചെയ്യൽ"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-mn/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-mn/strings.xml
index bc6e9ae..2074222 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-mn/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-mn/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Зөвшөөрсөн"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Зөвшөөрөөгүй"</string>
     <string name="version_text" msgid="4001669804596458577">"хувилбар <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> клон"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-mr/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-mr/strings.xml
index 142a60f..36ee416 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-mr/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-mr/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"अनुमती असलेले"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"अनुमती नाही"</string>
     <string name="version_text" msgid="4001669804596458577">"आवृत्ती <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> क्लोन"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ms/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ms/strings.xml
index 0d39435..c2f3243 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ms/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ms/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Dibenarkan"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Tidak dibenarkan"</string>
     <string name="version_text" msgid="4001669804596458577">"versi <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-my/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-my/strings.xml
index f87608f..2d08eec 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-my/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-my/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"ခွင့်ပြုထားသည်"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ခွင့်ပြုမထားပါ"</string>
     <string name="version_text" msgid="4001669804596458577">"ဗားရှင်း <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ပုံတူပွား"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-nb/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-nb/strings.xml
index 745834d..be739a2 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-nb/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-nb/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Tillatt"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Ikke tillatt"</string>
     <string name="version_text" msgid="4001669804596458577">"versjon <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon av <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ne/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ne/strings.xml
index 4b99bae1..7f3a523 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ne/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ne/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"अनुमति दिइएका एप"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"अनुमति नदिइएका एप"</string>
     <string name="version_text" msgid="4001669804596458577">"संस्करण <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> क्लोन"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-nl/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-nl/strings.xml
index 9a6b767..4ea7fed 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-nl/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-nl/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Toegestaan"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Niet toegestaan"</string>
     <string name="version_text" msgid="4001669804596458577">"versie <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Kloon van <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-or/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-or/strings.xml
index e562d55..501dc5c 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-or/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-or/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"ଅନୁମତି ଦିଆଯାଇଛି"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ଅନୁମତି ଦିଆଯାଇନାହିଁ"</string>
     <string name="version_text" msgid="4001669804596458577">"ଭର୍ସନ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> କ୍ଲୋନ"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-pa/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-pa/strings.xml
index a24ec90..28fcf0b 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-pa/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-pa/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"ਆਗਿਆ ਹੈ"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ਆਗਿਆ ਨਹੀਂ ਹੈ"</string>
     <string name="version_text" msgid="4001669804596458577">"ਵਰਜਨ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ਦਾ ਕਲੋਨ"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-pl/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-pl/strings.xml
index 7da29a5..c947a66 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-pl/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-pl/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Dozwolone"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Niedozwolone"</string>
     <string name="version_text" msgid="4001669804596458577">"wersja <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Klonuj: <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-pt-rBR/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-pt-rBR/strings.xml
index 85a42e2..ba92ebb6 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-pt-rBR/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitido"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Não permitido"</string>
     <string name="version_text" msgid="4001669804596458577">"Versão <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-pt-rPT/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-pt-rPT/strings.xml
index 9db985d..e7030df 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-pt-rPT/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitida"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Não permitida"</string>
     <string name="version_text" msgid="4001669804596458577">"versão <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-pt/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-pt/strings.xml
index 85a42e2..ba92ebb6 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-pt/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-pt/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permitido"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Não permitido"</string>
     <string name="version_text" msgid="4001669804596458577">"Versão <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ro/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ro/strings.xml
index e808e4a..c78e9be 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ro/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ro/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Permisă"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nepermisă"</string>
     <string name="version_text" msgid="4001669804596458577">"versiunea <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Clonă pentru <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ru/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ru/strings.xml
index 404ed31..3507bc7 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ru/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ru/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Разрешено"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Запрещено"</string>
     <string name="version_text" msgid="4001669804596458577">"версия <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Клон приложения \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\""</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-si/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-si/strings.xml
index 276cbc4..6014e07 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-si/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-si/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"ඉඩ දුන්"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ඉඩ නොදෙන"</string>
     <string name="version_text" msgid="4001669804596458577">"<xliff:g id="VERSION_NUM">%1$s</xliff:g> අනුවාදය"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ක්ලෝනය"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-sk/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-sk/strings.xml
index d8dde40..9888125 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-sk/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-sk/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Povolené"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nepovolené"</string>
     <string name="version_text" msgid="4001669804596458577">"verzia <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-sl/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-sl/strings.xml
index 4cb2a40..74b3ffd 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-sl/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-sl/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Dovoljeno"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Ni dovoljeno"</string>
     <string name="version_text" msgid="4001669804596458577">"različica <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Klonirani paket <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-sq/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-sq/strings.xml
index ba3d3cb..37aa7f0 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-sq/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-sq/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Lejohet"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Nuk lejohet"</string>
     <string name="version_text" msgid="4001669804596458577">"versioni <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon i <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-sr/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-sr/strings.xml
index 75caa5a..fe9b323 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-sr/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-sr/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Дозвољено"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Није дозвољено"</string>
     <string name="version_text" msgid="4001669804596458577">"верзија <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Клон апликације <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-sv/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-sv/strings.xml
index e11bb12..189c26e 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-sv/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-sv/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Tillåts"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Tillåts inte"</string>
     <string name="version_text" msgid="4001669804596458577">"version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Klon av <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-sw/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-sw/strings.xml
index be04d8e..241376b 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-sw/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-sw/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Inaruhusiwa"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Hairuhusiwi"</string>
     <string name="version_text" msgid="4001669804596458577">"toleo la <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Nakala ya <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ta/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ta/strings.xml
index cab94e2..b8e5d3a 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ta/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ta/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"அனுமதிக்கப்பட்டது"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"அனுமதிக்கப்படவில்லை"</string>
     <string name="version_text" msgid="4001669804596458577">"பதிப்பு <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> குளோன்"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-te/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-te/strings.xml
index 721e86a..db41032 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-te/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-te/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"అనుమతించబడినవి"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"అనుమతించబడలేదు"</string>
     <string name="version_text" msgid="4001669804596458577">"వెర్షన్ <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> క్లోన్"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-th/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-th/strings.xml
index 0844e24..ac38443 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-th/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-th/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"อนุญาต"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"ไม่อนุญาต"</string>
     <string name="version_text" msgid="4001669804596458577">"เวอร์ชัน <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"โคลนของ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-tl/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-tl/strings.xml
index 1d0bead..065834b 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-tl/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-tl/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Pinapahintulutan"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Hindi pinapahintulutan"</string>
     <string name="version_text" msgid="4001669804596458577">"bersyon <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone ng <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-tr/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-tr/strings.xml
index e3fcf4d..188a096 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-tr/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-tr/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"İzin veriliyor"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"İzin verilmiyor"</string>
     <string name="version_text" msgid="4001669804596458577">"sürüm: <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> klonu"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-uk/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-uk/strings.xml
index 392738c..0a5ca4f 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-uk/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-uk/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Дозволено"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Заборонено"</string>
     <string name="version_text" msgid="4001669804596458577">"версія <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Копія додатка <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-ur/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-ur/strings.xml
index c05c387..6b344fc 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-ur/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-ur/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"اجازت ہے"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"اجازت نہیں ہے"</string>
     <string name="version_text" msgid="4001669804596458577">"ورژن <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> کلون"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-vi/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-vi/strings.xml
index dbef24b..d5cf2b2 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-vi/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-vi/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Được phép"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Không được phép"</string>
     <string name="version_text" msgid="4001669804596458577">"phiên bản <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"Bản sao của <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-zh-rCN/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-zh-rCN/strings.xml
index fbd6fc7..fd16dea 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-zh-rCN/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"已允许"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"不允许"</string>
     <string name="version_text" msgid="4001669804596458577">"版本 <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>克隆"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-zh-rHK/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-zh-rHK/strings.xml
index c05e560..98071b9 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-zh-rHK/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"允許"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"不允許"</string>
     <string name="version_text" msgid="4001669804596458577">"版本 <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」複製本"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-zh-rTW/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-zh-rTW/strings.xml
index c05e560..03efe37 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-zh-rTW/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"允許"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"不允許"</string>
     <string name="version_text" msgid="4001669804596458577">"版本 <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」副本"</string>
 </resources>
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-zu/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-zu/strings.xml
index 45d11cc..088bb0f 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-zu/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-zu/strings.xml
@@ -23,6 +23,5 @@
     <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Kuvumelekile"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Akuvumelekile"</string>
     <string name="version_text" msgid="4001669804596458577">"Uhlobo <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
-    <!-- no translation found for cloned_app_info_label (1765651167024478391) -->
-    <skip />
+    <string name="cloned_app_info_label" msgid="1765651167024478391">"I-<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ye-clone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index a188fea..f9745e0 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -309,8 +309,8 @@
     <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"প্ৰতিটো লগ বাফাৰত ল\'গাৰৰ আকাৰ বাছনি কৰক"</string>
     <string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"লগাৰৰ স্থায়ী ষ্ট’ৰেজৰ বস্তুবোৰ মচিবনে?"</string>
     <string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"পাৰ্ছিছটেণ্ট লগাৰ ব্যৱহাৰ কৰ নিৰীক্ষণ নকৰাৰ সময়ত, আমি আপোনাৰ ডিভাইচত থকা লগাৰ ডেটা নিৱাসীক মচা দৰকাৰ।"</string>
-    <string name="select_logpersist_title" msgid="447071974007104196">"ডিভাইচটোত লগাৰৰ ডেটা নিৰবচ্ছিন্নভাৱে সঞ্চয় কৰক"</string>
-    <string name="select_logpersist_dialog_title" msgid="7745193591195485594">"ডিভাইচত স্থায়ীভাৱে সঞ্চয় কৰিবলৈ লগ বাফাৰবোৰ বাছনি কৰক"</string>
+    <string name="select_logpersist_title" msgid="447071974007104196">"ডিভাইচটোত লগাৰৰ ডেটা নিৰবচ্ছিন্নভাৱে ষ্ট’ৰ কৰক"</string>
+    <string name="select_logpersist_dialog_title" msgid="7745193591195485594">"ডিভাইচত স্থায়ীভাৱে ষ্ট’ৰ কৰিবলৈ লগ বাফাৰবোৰ বাছনি কৰক"</string>
     <string name="select_usb_configuration_title" msgid="6339801314922294586">"ইউএছবি কনফিগাৰেশ্বন বাছনি কৰক"</string>
     <string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"ইউএছবি কনফিগাৰেশ্বন বাছনি কৰক"</string>
     <string name="allow_mock_location" msgid="2102650981552527884">"নকল অৱস্থানৰ অনুমতি দিয়ক"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 80d90b7..2d01b37 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -173,7 +173,7 @@
     <string name="running_process_item_user_label" msgid="3988506293099805796">"Օգտատեր՝ <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
     <string name="launch_defaults_some" msgid="3631650616557252926">"Որոշ կանխադրված կարգավորումներ կան"</string>
     <string name="launch_defaults_none" msgid="8049374306261262709">"Կանխադրված կարգավորումներ չկան"</string>
-    <string name="tts_settings" msgid="8130616705989351312">"Տեքստի հնչեցման կարգավորումներ"</string>
+    <string name="tts_settings" msgid="8130616705989351312">"Տեքստի հնչեցման կարգա­վորումներ"</string>
     <string name="tts_settings_title" msgid="7602210956640483039">"Տեքստի հնչեցում"</string>
     <string name="tts_default_rate_title" msgid="3964187817364304022">"Խոսքի արագությունը"</string>
     <string name="tts_default_rate_summary" msgid="3781937042151716987">"Տեքստի արտասանման արագությունը"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 23df3be..b2a8459 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -173,7 +173,7 @@
     <string name="running_process_item_user_label" msgid="3988506293099805796">"Колдонуучу: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
     <string name="launch_defaults_some" msgid="3631650616557252926">"Айрым демейки параметрлер туураланды"</string>
     <string name="launch_defaults_none" msgid="8049374306261262709">"Демейки маанилер коюлган жок"</string>
-    <string name="tts_settings" msgid="8130616705989351312">"Кеп синтезаторунун жөндөөлөрү"</string>
+    <string name="tts_settings" msgid="8130616705989351312">"Кеп синтезаторунун параметрлери"</string>
     <string name="tts_settings_title" msgid="7602210956640483039">"Кеп синтезатору"</string>
     <string name="tts_default_rate_title" msgid="3964187817364304022">"Кеп ылдамдыгы"</string>
     <string name="tts_default_rate_summary" msgid="3781937042151716987">"Текст айтылчу ылдамдык"</string>
@@ -219,9 +219,9 @@
     <string name="development_settings_enable" msgid="4285094651288242183">"Иштеп чыгуучунун параметрлерин иштетүү"</string>
     <string name="development_settings_summary" msgid="8718917813868735095">"Колдонмо өндүрүү мүмкүнчүлүктөрүн орнотуу"</string>
     <string name="development_settings_not_available" msgid="355070198089140951">"Бул колдонуучуга өнүктүүрүүчү мүмкүнчүлүктөрү берилген эмес."</string>
-    <string name="vpn_settings_not_available" msgid="2894137119965668920">"Бул колдонуучу VPN жөндөөлөрүн колдоно албайт"</string>
-    <string name="tethering_settings_not_available" msgid="266821736434699780">"Бул колдонуучу модем режиминин жөндөөлөрүн өзгөртө албайт"</string>
-    <string name="apn_settings_not_available" msgid="1147111671403342300">"Бул колдонуучу мүмкүндүк алуу түйүнүнүн аталышынын жөндөөлөрүн колдоно албайт"</string>
+    <string name="vpn_settings_not_available" msgid="2894137119965668920">"Бул колдонуучу VPN параметрлерин колдоно албайт"</string>
+    <string name="tethering_settings_not_available" msgid="266821736434699780">"Бул колдонуучу модем режиминин параметрлерин өзгөртө албайт"</string>
+    <string name="apn_settings_not_available" msgid="1147111671403342300">"Бул колдонуучу мүмкүндүк алуу түйүнүнүн аталышынын параметрлерин колдоно албайт"</string>
     <string name="enable_adb" msgid="8072776357237289039">"USB аркылуу мүчүлүштүктөрдү аныктоо"</string>
     <string name="enable_adb_summary" msgid="3711526030096574316">"USB компьютерге сайылганда мүчүлүштүктөрдү оңдоо режими иштейт"</string>
     <string name="clear_adb_keys" msgid="3010148733140369917">"USB аркылуу мүчүлүштүктөрдү аныктоо уруксатын артка кайтаруу"</string>
@@ -322,7 +322,7 @@
     <string name="adb_warning_message" msgid="8145270656419669221">"USB-жөндөө - өндүрүү максатында гана  түзүлгөн. Аны компүтериңиз менен түзмөгүңүздүн ортосунда берилиштерди алмашуу, түзмөгүңүзгө колдонмолорду эскертүүсүз орнотуу жана лог берилиштерин окуу үчүн колдонсоңуз болот."</string>
     <string name="adbwifi_warning_title" msgid="727104571653031865">"Мүчүлүштүктөрдү Wi-Fi аркылуу оңдоого уруксат бересизби?"</string>
     <string name="adbwifi_warning_message" msgid="8005936574322702388">"Мүчүлүштүктөрдү Wi-Fi аркылуу аныктоо – өндүрүү максатында гана түзүлгөн. Аны компьютериңиз менен түзмөгүңүздүн ортосунда маалыматты алмашуу, колдонмолорду түзмөгүңүзгө эскертүүсүз орнотуу жана маалыматтар таржымалын окуу үчүн колдонсоңуз болот."</string>
-    <string name="adb_keys_warning_message" msgid="2968555274488101220">"Сиз мурун USB жөндөөлөрүнө уруксат берген бардык компүтерлердин жеткиси жокко чыгарылсынбы?"</string>
+    <string name="adb_keys_warning_message" msgid="2968555274488101220">"Сиз мурун USB параметрлерине уруксат берген бардык компүтерлердин жеткиси жокко чыгарылсынбы?"</string>
     <string name="dev_settings_warning_title" msgid="8251234890169074553">"Параметрлерди өзгөртүү"</string>
     <string name="dev_settings_warning_message" msgid="37741686486073668">"Бул орнотуулар өндүрүүчүлөр үчүн гана берилген. Булар түзмөгүңүздүн колдонмолорун бузулушуна же туура эмес иштешине алып келиши мүмкүн."</string>
     <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Орнотулуучу колдонмону текшерүү"</string>
@@ -420,7 +420,7 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Иштеген жок. Күйгүзүү үчүн басып коюңуз."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Иштеп турат. Өчүрүү үчүн басып коюңуз."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Көшүү режиминдеги колдонмонун абалы:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="2581975870429850549">"Медиа файлдарды транскоддоо жөндөөлөрү"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Медиа файлдарды транскоддоо параметрлери"</string>
     <string name="transcode_user_control" msgid="6176368544817731314">"Демейки жүргүзүлгөн транскоддоону өзгөртүп коюу"</string>
     <string name="transcode_enable_all" msgid="2411165920039166710">"Транскоддоо жүргүзүүнү иштетүү"</string>
     <string name="transcode_default" msgid="3784803084573509491">"Колдонмолордо заманбап форматтар колдоого алынат"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 166b3f0..c148eba 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -173,7 +173,7 @@
     <string name="running_process_item_user_label" msgid="3988506293099805796">"အသုံးပြုသူ- <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
     <string name="launch_defaults_some" msgid="3631650616557252926">"မူရင်းအချို့ သတ်မှတ်ပြီး"</string>
     <string name="launch_defaults_none" msgid="8049374306261262709">"မူရင်း သတ်မှတ်မထားပါ။"</string>
-    <string name="tts_settings" msgid="8130616705989351312">"စာ-မှ-စကားပြောင်းခြင်း ဆက်တင်များ"</string>
+    <string name="tts_settings" msgid="8130616705989351312">"စာ-မှ-စကား ဆက်တင်များ"</string>
     <string name="tts_settings_title" msgid="7602210956640483039">"စာ-မှ-စကားသို့ အထွက်"</string>
     <string name="tts_default_rate_title" msgid="3964187817364304022">"စကားပြောနှုန်း"</string>
     <string name="tts_default_rate_summary" msgid="3781937042151716987">"စာတမ်းအားပြောဆိုသော အမြန်နှုန်း"</string>
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/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b4598bf..e8fd226 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -369,6 +369,9 @@
     <!-- Permission needed to test wallpapers supporting ambient mode -->
     <uses-permission android:name="android.permission.AMBIENT_WALLPAPER" />
 
+    <!-- Permission needed to test wallpaper read methods -->
+    <uses-permission android:name="android.permission.READ_WALLPAPER_INTERNAL" />
+
     <!-- Permission required to test ContentResolver caching. -->
     <uses-permission android:name="android.permission.CACHE_CONTENT" />
 
@@ -802,6 +805,9 @@
     <!-- Permissions required for CTS test - CtsBroadcastRadioTestCases -->
     <uses-permission android:name="android.permission.ACCESS_BROADCAST_RADIO" />
 
+    <!-- Permission required for CTS test - ActivityCaptureCallbackTests -->
+    <uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt
new file mode 100644
index 0000000..1c9dabb
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt
@@ -0,0 +1,6 @@
+package com.android.systemui.animation
+
+interface AnimationFeatureFlags {
+    val isPredictiveBackQsDialogAnim: Boolean
+        get() = false
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index a3ed085..e91a671 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -33,8 +33,13 @@
 import android.view.WindowManager
 import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
 import android.widget.FrameLayout
+import android.window.OnBackInvokedDispatcher
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.jank.InteractionJankMonitor.CujType
+import com.android.systemui.animation.back.BackAnimationSpec
+import com.android.systemui.animation.back.applyTo
+import com.android.systemui.animation.back.floatingSystemSurfacesForSysUi
+import com.android.systemui.animation.back.onBackAnimationCallbackFrom
 import kotlin.math.roundToInt
 
 private const val TAG = "DialogLaunchAnimator"
@@ -55,8 +60,9 @@
 constructor(
     private val callback: Callback,
     private val interactionJankMonitor: InteractionJankMonitor,
+    private val featureFlags: AnimationFeatureFlags,
     private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
-    private val isForTesting: Boolean = false
+    private val isForTesting: Boolean = false,
 ) {
     private companion object {
         private val TIMINGS = ActivityLaunchAnimator.TIMINGS
@@ -273,15 +279,16 @@
 
         val animatedDialog =
             AnimatedDialog(
-                launchAnimator,
-                callback,
-                interactionJankMonitor,
-                controller,
+                launchAnimator = launchAnimator,
+                callback = callback,
+                interactionJankMonitor = interactionJankMonitor,
+                controller = controller,
                 onDialogDismissed = { openedDialogs.remove(it) },
                 dialog = dialog,
-                animateBackgroundBoundsChange,
-                animatedParent,
-                isForTesting,
+                animateBackgroundBoundsChange = animateBackgroundBoundsChange,
+                parentAnimatedDialog = animatedParent,
+                forceDisableSynchronization = isForTesting,
+                featureFlags = featureFlags,
             )
 
         openedDialogs.add(animatedDialog)
@@ -517,6 +524,7 @@
      * Whether synchronization should be disabled, which can be useful if we are running in a test.
      */
     private val forceDisableSynchronization: Boolean,
+    private val featureFlags: AnimationFeatureFlags,
 ) {
     /**
      * The DecorView of this dialog window.
@@ -778,12 +786,45 @@
         // the dialog.
         dialog.setDismissOverride(this::onDialogDismissed)
 
+        if (featureFlags.isPredictiveBackQsDialogAnim) {
+            // TODO(b/265923095) Improve animations for QS dialogs on configuration change
+            registerOnBackInvokedCallback(targetView = dialogContentWithBackground)
+        }
+
         // Show the dialog.
         dialog.show()
-
         moveSourceDrawingToDialog()
     }
 
+    private fun registerOnBackInvokedCallback(targetView: View) {
+        val metrics = targetView.resources.displayMetrics
+
+        val onBackAnimationCallback =
+            onBackAnimationCallbackFrom(
+                backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi(metrics),
+                displayMetrics = metrics, // TODO(b/265060720): We could remove this
+                onBackProgressed = { backTransformation -> backTransformation.applyTo(targetView) },
+                onBackInvoked = { dialog.dismiss() },
+            )
+
+        val dispatcher = dialog.onBackInvokedDispatcher
+        targetView.addOnAttachStateChangeListener(
+            object : View.OnAttachStateChangeListener {
+                override fun onViewAttachedToWindow(v: View) {
+                    dispatcher.registerOnBackInvokedCallback(
+                        OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+                        onBackAnimationCallback
+                    )
+                }
+
+                override fun onViewDetachedFromWindow(v: View) {
+                    targetView.removeOnAttachStateChangeListener(this)
+                    dispatcher.unregisterOnBackInvokedCallback(onBackAnimationCallback)
+                }
+            }
+        )
+    }
+
     private fun moveSourceDrawingToDialog() {
         if (decorView.viewRootImpl == null) {
             // Make sure that we have access to the dialog view root to move the drawing to the
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 681e4d1..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.
      *
@@ -133,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)
@@ -143,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)
@@ -155,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
@@ -171,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.
@@ -202,9 +193,8 @@
      *         // progress is 0.5
      *         animator.start()
      *     }
-     * </code>
-     * </pre>
-     *
+     * ```
+     * </code> </pre>
      */
     fun rebase() {
         if (progress == 0f) {
@@ -266,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) {
@@ -360,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
@@ -391,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
             }
@@ -407,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(
@@ -421,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 ->
@@ -439,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"
                         }
                     }
 
@@ -447,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) {
@@ -483,10 +477,7 @@
     }
 
     // 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.
@@ -500,10 +491,13 @@
             }
 
             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) {
@@ -517,8 +511,8 @@
 }
 
 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/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
new file mode 100644
index 0000000..f3d8b17
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.animation.back
+
+import android.util.DisplayMetrics
+import android.view.animation.Interpolator
+import android.window.BackEvent
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.util.dpToPx
+
+/** Used to convert [BackEvent] into a [BackTransformation]. */
+fun interface BackAnimationSpec {
+
+    /** Computes transformation based on a [backEvent] and sets it to [result]. */
+    fun getBackTransformation(
+        backEvent: BackEvent,
+        progressY: Float, // TODO(b/265060720): Remove progressY. Could be retrieved from backEvent
+        result: BackTransformation,
+    )
+
+    companion object
+}
+
+/** Create a [BackAnimationSpec] from [displayMetrics] and design specs. */
+fun BackAnimationSpec.Companion.createFloatingSurfaceAnimationSpec(
+    displayMetrics: DisplayMetrics,
+    maxMarginXdp: Float,
+    maxMarginYdp: Float,
+    minScale: Float,
+    translateXEasing: Interpolator = Interpolators.STANDARD_DECELERATE,
+    translateYEasing: Interpolator = Interpolators.LINEAR,
+    scaleEasing: Interpolator = Interpolators.STANDARD_DECELERATE,
+): BackAnimationSpec {
+    val screenWidthPx = displayMetrics.widthPixels
+    val screenHeightPx = displayMetrics.heightPixels
+
+    val maxMarginXPx = maxMarginXdp.dpToPx(displayMetrics)
+    val maxMarginYPx = maxMarginYdp.dpToPx(displayMetrics)
+    val maxTranslationXByScale = (screenWidthPx - screenWidthPx * minScale) / 2
+    val maxTranslationX = maxTranslationXByScale - maxMarginXPx
+    val maxTranslationYByScale = (screenHeightPx - screenHeightPx * minScale) / 2
+    val maxTranslationY = maxTranslationYByScale - maxMarginYPx
+    val minScaleReversed = 1f - minScale
+
+    return BackAnimationSpec { backEvent, progressY, result ->
+        val direction = if (backEvent.swipeEdge == BackEvent.EDGE_LEFT) 1 else -1
+        val progressX = backEvent.progress
+
+        val ratioTranslateX = translateXEasing.getInterpolation(progressX)
+        val ratioTranslateY = translateYEasing.getInterpolation(progressY)
+        val ratioScale = scaleEasing.getInterpolation(progressX)
+
+        result.apply {
+            translateX = ratioTranslateX * direction * maxTranslationX
+            translateY = ratioTranslateY * maxTranslationY
+            scale = 1f - (ratioScale * minScaleReversed)
+        }
+    }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt
new file mode 100644
index 0000000..c6b7073
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.animation.back
+
+import android.util.DisplayMetrics
+
+/**
+ * SysUI transitions - Dismiss app (ST1) Return to launching surface or place of origin
+ * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-1-dismiss-app
+ */
+fun BackAnimationSpec.Companion.dismissAppForSysUi(
+    displayMetrics: DisplayMetrics,
+): BackAnimationSpec =
+    BackAnimationSpec.createFloatingSurfaceAnimationSpec(
+        displayMetrics = displayMetrics,
+        maxMarginXdp = 8f,
+        maxMarginYdp = 8f,
+        minScale = 0.8f,
+    )
+
+/**
+ * SysUI transitions - Cross task (ST2) Return to previous task/app, keeping the current one open
+ * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-2-cross-task
+ */
+fun BackAnimationSpec.Companion.crossTaskForSysUi(
+    displayMetrics: DisplayMetrics,
+): BackAnimationSpec =
+    BackAnimationSpec.createFloatingSurfaceAnimationSpec(
+        displayMetrics = displayMetrics,
+        maxMarginXdp = 8f,
+        maxMarginYdp = 8f,
+        minScale = 0.8f,
+    )
+
+/**
+ * SysUI transitions - Inner area dismiss (ST3) Dismiss non-detachable surface
+ * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-3-inner-area-dismiss
+ */
+fun BackAnimationSpec.Companion.innerAreaDismissForSysUi(
+    displayMetrics: DisplayMetrics,
+): BackAnimationSpec =
+    BackAnimationSpec.createFloatingSurfaceAnimationSpec(
+        displayMetrics = displayMetrics,
+        maxMarginXdp = 0f,
+        maxMarginYdp = 0f,
+        minScale = 0.9f,
+    )
+
+/**
+ * SysUI transitions - Floating system surfaces (ST4)
+ * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-4-floating-system-surfaces
+ */
+fun BackAnimationSpec.Companion.floatingSystemSurfacesForSysUi(
+    displayMetrics: DisplayMetrics,
+): BackAnimationSpec =
+    BackAnimationSpec.createFloatingSurfaceAnimationSpec(
+        displayMetrics = displayMetrics,
+        maxMarginXdp = 8f,
+        maxMarginYdp = 8f,
+        minScale = 0.8f,
+    )
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackTransformation.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackTransformation.kt
new file mode 100644
index 0000000..49d1fb4
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackTransformation.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.animation.back
+
+import android.view.View
+
+/**
+ * This object that represents the transformation to apply to the target. The properties of this
+ * object are mutable for performance reasons (avoid recreating this object)
+ */
+data class BackTransformation(
+    var translateX: Float = Float.NaN,
+    var translateY: Float = Float.NaN,
+    var scale: Float = Float.NaN,
+)
+
+/** Apply the transformation to the [targetView] */
+fun BackTransformation.applyTo(targetView: View) {
+    if (translateX.isFinite()) targetView.translationX = translateX
+    if (translateY.isFinite()) targetView.translationY = translateY
+    if (scale.isFinite()) {
+        targetView.scaleX = scale
+        targetView.scaleY = scale
+    }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt
new file mode 100644
index 0000000..33d14b1
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.animation.back
+
+import android.util.DisplayMetrics
+import android.window.BackEvent
+import android.window.OnBackAnimationCallback
+
+/**
+ * Generates an [OnBackAnimationCallback] given a [backAnimationSpec]. [onBackProgressed] will be
+ * called on each update passing the current [BackTransformation].
+ *
+ * Optionally, you can specify [onBackStarted], [onBackInvoked], and [onBackCancelled] callbacks.
+ */
+fun onBackAnimationCallbackFrom(
+    backAnimationSpec: BackAnimationSpec,
+    displayMetrics: DisplayMetrics, // TODO(b/265060720): We could remove this
+    onBackProgressed: (BackTransformation) -> Unit,
+    onBackStarted: (BackEvent) -> Unit = {},
+    onBackInvoked: () -> Unit = {},
+    onBackCancelled: () -> Unit = {},
+): OnBackAnimationCallback {
+    return object : OnBackAnimationCallback {
+        private var initialY = 0f
+        private val lastTransformation = BackTransformation()
+
+        override fun onBackStarted(backEvent: BackEvent) {
+            initialY = backEvent.touchY
+            onBackStarted(backEvent)
+        }
+
+        override fun onBackProgressed(backEvent: BackEvent) {
+            val progressY = (backEvent.touchY - initialY) / displayMetrics.heightPixels
+
+            backAnimationSpec.getBackTransformation(
+                backEvent = backEvent,
+                progressY = progressY,
+                result = lastTransformation,
+            )
+
+            onBackProgressed(lastTransformation)
+        }
+
+        override fun onBackInvoked() {
+            onBackInvoked()
+        }
+
+        override fun onBackCancelled() {
+            onBackCancelled()
+        }
+    }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/util/Dimension.kt b/packages/SystemUI/animation/src/com/android/systemui/util/Dimension.kt
new file mode 100644
index 0000000..4bc9972
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/util/Dimension.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.util
+
+import android.content.Context
+import android.content.res.Resources
+import android.util.DisplayMetrics
+import android.util.TypedValue
+
+/** Convert [this] number of dps to device pixels. */
+fun Number.dpToPx(context: Context): Float = dpToPx(resources = context.resources)
+
+/** Convert [this] number of dps to device pixels. */
+fun Number.dpToPx(resources: Resources): Float = dpToPx(displayMetrics = resources.displayMetrics)
+
+/** Convert [this] number of dps to device pixels. */
+fun Number.dpToPx(displayMetrics: DisplayMetrics): Float =
+    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, toFloat(), displayMetrics)
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/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
index 0ee813b..ef2247f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
@@ -15,39 +15,36 @@
  */
 package com.android.systemui.shared.regionsampling
 
+import android.app.WallpaperColors
+import android.app.WallpaperManager
 import android.graphics.Color
+import android.graphics.Point
 import android.graphics.Rect
+import android.graphics.RectF
 import android.view.View
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper
-import com.android.systemui.shared.navigationbar.RegionSamplingHelper.SamplingCallback
 import java.io.PrintWriter
 import java.util.concurrent.Executor
 
 /** Class for instance of RegionSamplingHelper */
-open class RegionSampler(
-    sampledView: View?,
+open class RegionSampler
+@JvmOverloads
+constructor(
+    val sampledView: View?,
     mainExecutor: Executor?,
-    bgExecutor: Executor?,
-    regionSamplingEnabled: Boolean,
-    updateFun: UpdateColorCallback
-) {
+    val bgExecutor: Executor?,
+    val regionSamplingEnabled: Boolean,
+    val updateForegroundColor: UpdateColorCallback,
+    val wallpaperManager: WallpaperManager? = WallpaperManager.getInstance(sampledView?.context)
+) : WallpaperManager.LocalWallpaperColorConsumer {
     private var regionDarkness = RegionDarkness.DEFAULT
     private var samplingBounds = Rect()
     private val tmpScreenLocation = IntArray(2)
     @VisibleForTesting var regionSampler: RegionSamplingHelper? = null
     private var lightForegroundColor = Color.WHITE
     private var darkForegroundColor = Color.BLACK
-
-    @VisibleForTesting
-    open fun createRegionSamplingHelper(
-        sampledView: View,
-        callback: SamplingCallback,
-        mainExecutor: Executor?,
-        bgExecutor: Executor?
-    ): RegionSamplingHelper {
-        return RegionSamplingHelper(sampledView, callback, mainExecutor, bgExecutor)
-    }
+    private val displaySize = Point()
 
     /**
      * Sets the colors to be used for Dark and Light Foreground.
@@ -73,7 +70,7 @@
         }
     }
 
-    private fun convertToClockDarkness(isRegionDark: Boolean): RegionDarkness {
+    private fun getRegionDarkness(isRegionDark: Boolean): RegionDarkness {
         return if (isRegionDark) {
             RegionDarkness.DARK
         } else {
@@ -87,12 +84,32 @@
 
     /** Start region sampler */
     fun startRegionSampler() {
-        regionSampler?.start(samplingBounds)
+        if (!regionSamplingEnabled || sampledView == null) {
+            return
+        }
+
+        val sampledRegion = calculateSampledRegion(sampledView)
+        val regions = ArrayList<RectF>()
+        val sampledRegionWithOffset = convertBounds(sampledRegion)
+        regions.add(sampledRegionWithOffset)
+
+        wallpaperManager?.removeOnColorsChangedListener(this)
+        wallpaperManager?.addOnColorsChangedListener(this, regions)
+
+        // TODO(b/265969235): conditionally set FLAG_LOCK or FLAG_SYSTEM once HS smartspace
+        // implemented
+        bgExecutor?.execute(
+            Runnable {
+                val initialSampling =
+                    wallpaperManager?.getWallpaperColors(WallpaperManager.FLAG_LOCK)
+                onColorsChanged(sampledRegionWithOffset, initialSampling)
+            }
+        )
     }
 
     /** Stop region sampler */
     fun stopRegionSampler() {
-        regionSampler?.stop()
+        wallpaperManager?.removeOnColorsChangedListener(this)
     }
 
     /** Dump region sampler */
@@ -100,43 +117,66 @@
         regionSampler?.dump(pw)
     }
 
-    init {
-        if (regionSamplingEnabled && sampledView != null) {
-            regionSampler =
-                createRegionSamplingHelper(
-                    sampledView,
-                    object : SamplingCallback {
-                        override fun onRegionDarknessChanged(isRegionDark: Boolean) {
-                            regionDarkness = convertToClockDarkness(isRegionDark)
-                            updateFun()
-                        }
-                        /**
-                         * The method getLocationOnScreen is used to obtain the view coordinates
-                         * relative to its left and top edges on the device screen. Directly
-                         * accessing the X and Y coordinates of the view returns the location
-                         * relative to its parent view instead.
-                         */
-                        override fun getSampledRegion(sampledView: View): Rect {
-                            val screenLocation = tmpScreenLocation
-                            sampledView.getLocationOnScreen(screenLocation)
-                            val left = screenLocation[0]
-                            val top = screenLocation[1]
-                            samplingBounds.left = left
-                            samplingBounds.top = top
-                            samplingBounds.right = left + sampledView.width
-                            samplingBounds.bottom = top + sampledView.height
-                            return samplingBounds
-                        }
+    fun calculateSampledRegion(sampledView: View): RectF {
+        val screenLocation = tmpScreenLocation
+        /**
+         * The method getLocationOnScreen is used to obtain the view coordinates relative to its
+         * left and top edges on the device screen. Directly accessing the X and Y coordinates of
+         * the view returns the location relative to its parent view instead.
+         */
+        sampledView.getLocationOnScreen(screenLocation)
+        val left = screenLocation[0]
+        val top = screenLocation[1]
 
-                        override fun isSamplingEnabled(): Boolean {
-                            return regionSamplingEnabled
-                        }
-                    },
-                    mainExecutor,
-                    bgExecutor
-                )
-        }
-        regionSampler?.setWindowVisible(true)
+        samplingBounds.left = left
+        samplingBounds.top = top
+        samplingBounds.right = left + sampledView.width
+        samplingBounds.bottom = top + sampledView.height
+
+        return RectF(samplingBounds)
+    }
+
+    /**
+     * Convert the bounds of the region we want to sample from to fractional offsets because
+     * WallpaperManager requires the bounds to be between [0,1]. The wallpaper is treated as one
+     * continuous image, so if there are multiple screens, then each screen falls into a fractional
+     * range. For instance, 4 screens have the ranges [0, 0.25], [0,25, 0.5], [0.5, 0.75], [0.75,
+     * 1].
+     */
+    fun convertBounds(originalBounds: RectF): RectF {
+
+        // TODO(b/265969235): GRAB # PAGES + CURRENT WALLPAPER PAGE # FROM LAUNCHER
+        // TODO(b/265968912): remove hard-coded value once LS wallpaper supported
+        val wallpaperPageNum = 0
+        val numScreens = 1
+
+        val screenWidth = displaySize.x
+        // TODO: investigate small difference between this and the height reported in go/web-hv
+        val screenHeight = displaySize.y
+
+        val newBounds = RectF()
+        // horizontal
+        newBounds.left = ((originalBounds.left / screenWidth) + wallpaperPageNum) / numScreens
+        newBounds.right = ((originalBounds.right / screenWidth) + wallpaperPageNum) / numScreens
+        // vertical
+        newBounds.top = originalBounds.top / screenHeight
+        newBounds.bottom = originalBounds.bottom / screenHeight
+
+        return newBounds
+    }
+
+    init {
+        sampledView?.context?.display?.getSize(displaySize)
+    }
+
+    override fun onColorsChanged(area: RectF?, colors: WallpaperColors?) {
+        // update text color when wallpaper color changes
+        regionDarkness =
+            getRegionDarkness(
+                (colors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) !=
+                    WallpaperColors.HINT_SUPPORTS_DARK_TEXT
+            )
+        updateForegroundColor()
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index a45ce42..1680b47 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -24,6 +24,7 @@
 import android.text.format.DateFormat
 import android.util.TypedValue
 import android.view.View
+import android.widget.FrameLayout
 import androidx.annotation.VisibleForTesting
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
@@ -47,18 +48,17 @@
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
 import com.android.systemui.statusbar.policy.ConfigurationController
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.launch
 import java.io.PrintWriter
 import java.util.Locale
 import java.util.TimeZone
 import java.util.concurrent.Executor
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.launch
 
 /**
  * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
@@ -89,7 +89,11 @@
                 value.largeClock.logBuffer = largeLogBuffer
 
                 value.initialize(resources, dozeAmount, 0f)
-                updateRegionSamplers(value)
+
+                if (regionSamplingEnabled) {
+                    clock?.smallClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener)
+                    clock?.largeClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener)
+                }
                 updateFontSizes()
             }
         }
@@ -104,47 +108,87 @@
     private var disposableHandle: DisposableHandle? = null
     private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING)
 
-    private fun updateColors() {
+    private val mLayoutChangedListener = object : View.OnLayoutChangeListener {
+        private var currentSmallClockView: View? = null
+        private var currentLargeClockView: View? = null
+        private var currentSmallClockLocation = IntArray(2)
+        private var currentLargeClockLocation = IntArray(2)
 
-        if (regionSamplingEnabled && smallRegionSampler != null && largeRegionSampler != null) {
-            val wallpaperManager = WallpaperManager.getInstance(context)
-            if (!wallpaperManager.lockScreenWallpaperExists()) {
-                smallClockIsDark = smallRegionSampler!!.currentRegionDarkness().isDark
-                largeClockIsDark = largeRegionSampler!!.currentRegionDarkness().isDark
-            }
-        } else {
-            val isLightTheme = TypedValue()
-            context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
-            smallClockIsDark = isLightTheme.data == 0
-            largeClockIsDark = isLightTheme.data == 0
+        override fun onLayoutChange(
+            view: View?,
+            left: Int,
+            top: Int,
+            right: Int,
+            bottom: Int,
+            oldLeft: Int,
+            oldTop: Int,
+            oldRight: Int,
+            oldBottom: Int
+        ) {
+        val parent = (view?.parent) as FrameLayout
+
+        // don't pass in negative bounds when clocks are in transition state
+        if (view.locationOnScreen[0] < 0 || view.locationOnScreen[1] < 0) {
+            return
         }
 
+        // SMALL CLOCK
+        if (parent.id == R.id.lockscreen_clock_view) {
+            // view bounds have changed due to clock size changing (i.e. different character widths)
+            // AND/OR the view has been translated when transitioning between small and large clock
+            if (view != currentSmallClockView ||
+                !view.locationOnScreen.contentEquals(currentSmallClockLocation)) {
+                currentSmallClockView = view
+                currentSmallClockLocation = view.locationOnScreen
+                updateRegionSampler(view)
+            }
+        }
+        // LARGE CLOCK
+        else if (parent.id == R.id.lockscreen_clock_view_large) {
+            if (view != currentLargeClockView ||
+                !view.locationOnScreen.contentEquals(currentLargeClockLocation)) {
+                currentLargeClockView = view
+                currentLargeClockLocation = view.locationOnScreen
+                updateRegionSampler(view)
+            }
+        }
+        }
+    }
+
+    private fun updateColors() {
+        val wallpaperManager = WallpaperManager.getInstance(context)
+        if (regionSamplingEnabled && !wallpaperManager.lockScreenWallpaperExists()) {
+            if (regionSampler != null) {
+                if (regionSampler?.sampledView == clock?.smallClock?.view) {
+                    smallClockIsDark = regionSampler!!.currentRegionDarkness().isDark
+                    clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark)
+                    return
+                } else if (regionSampler?.sampledView == clock?.largeClock?.view) {
+                    largeClockIsDark = regionSampler!!.currentRegionDarkness().isDark
+                    clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark)
+                    return
+                }
+            }
+        }
+
+        val isLightTheme = TypedValue()
+        context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
+        smallClockIsDark = isLightTheme.data == 0
+        largeClockIsDark = isLightTheme.data == 0
+
         clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark)
         clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark)
     }
 
-    private fun updateRegionSamplers(currentClock: ClockController?) {
-        smallRegionSampler?.stopRegionSampler()
-        largeRegionSampler?.stopRegionSampler()
-
-        smallRegionSampler = createRegionSampler(
-                currentClock?.smallClock?.view,
-                mainExecutor,
-                bgExecutor,
-                regionSamplingEnabled,
-                ::updateColors
-        )
-
-        largeRegionSampler = createRegionSampler(
-                currentClock?.largeClock?.view,
-                mainExecutor,
-                bgExecutor,
-                regionSamplingEnabled,
-                ::updateColors
-        )
-
-        smallRegionSampler!!.startRegionSampler()
-        largeRegionSampler!!.startRegionSampler()
+    private fun updateRegionSampler(sampledRegion: View) {
+        regionSampler?.stopRegionSampler()
+        regionSampler = createRegionSampler(
+            sampledRegion,
+            mainExecutor,
+            bgExecutor,
+            regionSamplingEnabled,
+            ::updateColors
+        )?.apply { startRegionSampler() }
 
         updateColors()
     }
@@ -155,7 +199,7 @@
             bgExecutor: Executor?,
             regionSamplingEnabled: Boolean,
             updateColors: () -> Unit
-    ): RegionSampler {
+    ): RegionSampler? {
         return RegionSampler(
             sampledView,
             mainExecutor,
@@ -164,8 +208,7 @@
             updateColors)
     }
 
-    var smallRegionSampler: RegionSampler? = null
-    var largeRegionSampler: RegionSampler? = null
+    var regionSampler: RegionSampler? = null
 
     private var smallClockIsDark = true
     private var largeClockIsDark = true
@@ -232,8 +275,6 @@
         configurationController.addCallback(configListener)
         batteryController.addCallback(batteryCallback)
         keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
-        smallRegionSampler?.startRegionSampler()
-        largeRegionSampler?.startRegionSampler()
         disposableHandle = parent.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 listenForDozing(this)
@@ -258,8 +299,7 @@
         configurationController.removeCallback(configListener)
         batteryController.removeCallback(batteryCallback)
         keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
-        smallRegionSampler?.stopRegionSampler()
-        largeRegionSampler?.stopRegionSampler()
+        regionSampler?.stopRegionSampler()
     }
 
     private fun updateFontSizes() {
@@ -275,8 +315,7 @@
     fun dump(pw: PrintWriter) {
         pw.println(this)
         clock?.dump(pw)
-        smallRegionSampler?.dump(pw)
-        largeRegionSampler?.dump(pw)
+        regionSampler?.dump(pw)
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index b159714..35cae09 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -47,7 +47,6 @@
 import com.android.systemui.R;
 
 import java.util.ArrayList;
-import java.util.Stack;
 
 /**
  * A View similar to a textView which contains password text and can animate when the text is
@@ -92,7 +91,6 @@
     private final int mGravity;
     private ArrayList<CharState> mTextChars = new ArrayList<>();
     private String mText = "";
-    private Stack<CharState> mCharPool = new Stack<>();
     private int mDotSize;
     private PowerManager mPM;
     private int mCharPadding;
@@ -310,13 +308,7 @@
     }
 
     private CharState obtainCharState(char c) {
-        CharState charState;
-        if(mCharPool.isEmpty()) {
-            charState = new CharState();
-        } else {
-            charState = mCharPool.pop();
-            charState.reset();
-        }
+        CharState charState = new CharState();
         charState.whichChar = c;
         return charState;
     }
@@ -343,8 +335,6 @@
                 maxDelay = Math.min(maxDelay, RESET_MAX_DELAY) + DISAPPEAR_DURATION;
                 charState.startRemoveAnimation(startDelay, maxDelay);
                 charState.removeDotSwapCallbacks();
-            } else {
-                mCharPool.push(charState);
             }
         }
         if (!animated) {
@@ -421,8 +411,6 @@
             public void onAnimationEnd(Animator animation) {
                 if (!mCancelled) {
                     mTextChars.remove(CharState.this);
-                    mCharPool.push(CharState.this);
-                    reset();
                     cancelAnimator(textTranslateAnimator);
                     textTranslateAnimator = null;
                 }
@@ -518,21 +506,6 @@
             }
         };
 
-        void reset() {
-            whichChar = 0;
-            currentTextSizeFactor = 0.0f;
-            currentDotSizeFactor = 0.0f;
-            currentWidthFactor = 0.0f;
-            cancelAnimator(textAnimator);
-            textAnimator = null;
-            cancelAnimator(dotAnimator);
-            dotAnimator = null;
-            cancelAnimator(widthAnimator);
-            widthAnimator = null;
-            currentTextTranslationY = 1.0f;
-            removeDotSwapCallbacks();
-        }
-
         void startRemoveAnimation(long startDelay, long widthDelay) {
             boolean dotNeedsAnimation = (currentDotSizeFactor > 0.0f && dotAnimator == null)
                     || (dotAnimator != null && dotAnimationIsGrowing);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 5d85fc96..768eeb8 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -65,8 +65,7 @@
     val FSI_ON_DND_UPDATE = unreleasedFlag(259130119, "fsi_on_dnd_update", teamfood = true)
 
     // TODO(b/265804648): Tracking Bug
-    @JvmField
-    val DISABLE_FSI = unreleasedFlag(265804648, "disable_fsi")
+    @JvmField val DISABLE_FSI = unreleasedFlag(265804648, "disable_fsi")
 
     // TODO(b/254512538): Tracking Bug
     val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply", teamfood = true)
@@ -104,7 +103,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 +216,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 +234,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
 
@@ -348,6 +353,9 @@
     val MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE =
         unreleasedFlag(912, "media_ttt_dismiss_gesture", teamfood = true)
 
+    // TODO(b/266157412): Tracking Bug
+    val MEDIA_RETAIN_SESSIONS = unreleasedFlag(913, "media_retain_sessions")
+
     // 1000 - dock
     val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
 
@@ -463,6 +471,21 @@
     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)
+
+    // TODO(b/265639042): Tracking Bug
+    @JvmField
+    val WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM =
+        unreleasedFlag(1209, "persist.wm.debug.predictive_back_qs_dialog_anim", teamfood = true)
+
     // 1300 - screenshots
     // TODO(b/254513155): Tracking Bug
     @JvmField
@@ -494,6 +517,8 @@
     // 1800 - shade container
     @JvmField
     val LEAVE_SHADE_OPEN_FOR_BUGREPORT = releasedFlag(1800, "leave_shade_open_for_bugreport")
+    // TODO(b/265944639): Tracking Bug
+    @JvmField val DUAL_SHADE = releasedFlag(1801, "dual_shade")
 
     // 1900
     @JvmField val NOTE_TASKS = unreleasedFlag(1900, "keycode_flag")
@@ -554,6 +579,5 @@
 
     // 2600 - keyboard shortcut
     // TODO(b/259352579): Tracking Bug
-    @JvmField
-    val SHORTCUT_LIST_SEARCH_LAYOUT = unreleasedFlag(2600, "shortcut_list_search_layout")
+    @JvmField val SHORTCUT_LIST_SEARCH_LAYOUT = unreleasedFlag(2600, "shortcut_list_search_layout")
 }
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/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 6a5e725..da2164e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -304,6 +304,7 @@
         mediaTimeoutListener.stateCallback = { key: String, state: PlaybackState ->
             updateState(key, state)
         }
+        mediaTimeoutListener.sessionCallback = { key: String -> onSessionDestroyed(key) }
         mediaResumeListener.setManager(this)
         mediaDataFilter.mediaDataManager = this
 
@@ -1292,45 +1293,106 @@
 
     fun onNotificationRemoved(key: String) {
         Assert.isMainThread()
-        val removed = mediaEntries.remove(key)
-        if (useMediaResumption && removed?.resumeAction != null && removed.isLocalSession()) {
-            Log.d(TAG, "Not removing $key because resumable")
-            // Move to resume key (aka package name) if that key doesn't already exist.
-            val resumeAction = getResumeMediaAction(removed.resumeAction!!)
-            val updated =
-                removed.copy(
-                    token = null,
-                    actions = listOf(resumeAction),
-                    semanticActions = MediaButton(playOrPause = resumeAction),
-                    actionsToShowInCompact = listOf(0),
-                    active = false,
-                    resumption = true,
-                    isPlaying = false,
-                    isClearable = true
-                )
-            val pkg = removed.packageName
-            val migrate = mediaEntries.put(pkg, updated) == null
-            // Notify listeners of "new" controls when migrating or removed and update when not
-            if (migrate) {
-                notifyMediaDataLoaded(pkg, key, updated)
-            } else {
-                // Since packageName is used for the key of the resumption controls, it is
-                // possible that another notification has already been reused for the resumption
-                // controls of this package. In this case, rather than renaming this player as
-                // packageName, just remove it and then send a update to the existing resumption
-                // controls.
-                notifyMediaDataRemoved(key)
-                notifyMediaDataLoaded(pkg, pkg, updated)
-            }
-            logger.logActiveConvertedToResume(updated.appUid, pkg, updated.instanceId)
-            return
-        }
-        if (removed != null) {
+        val removed = mediaEntries.remove(key) ?: return
+
+        if (useMediaResumption && removed.resumeAction != null && removed.isLocalSession()) {
+            convertToResumePlayer(removed)
+        } else if (mediaFlags.isRetainingPlayersEnabled()) {
+            handlePossibleRemoval(removed, notificationRemoved = true)
+        } else {
             notifyMediaDataRemoved(key)
             logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId)
         }
     }
 
+    private fun onSessionDestroyed(key: String) {
+        if (!mediaFlags.isRetainingPlayersEnabled()) return
+
+        if (DEBUG) Log.d(TAG, "session destroyed for $key")
+        val entry = mediaEntries.remove(key) ?: return
+        // Clear token since the session is no longer valid
+        val updated = entry.copy(token = null)
+        handlePossibleRemoval(updated)
+    }
+
+    /**
+     * Convert to resume state if the player is no longer valid and active, then notify listeners
+     * that the data was updated. Does not convert to resume state if the player is still valid, or
+     * if it was removed before becoming inactive. (Assumes that [removed] was removed from
+     * [mediaEntries] before this function was called)
+     */
+    private fun handlePossibleRemoval(removed: MediaData, notificationRemoved: Boolean = false) {
+        val key = removed.notificationKey!!
+        val hasSession = removed.token != null
+        if (hasSession && removed.semanticActions != null) {
+            // The app was using session actions, and the session is still valid: keep player
+            if (DEBUG) Log.d(TAG, "Notification removed but using session actions $key")
+            mediaEntries.put(key, removed)
+            notifyMediaDataLoaded(key, key, removed)
+        } else if (!notificationRemoved && removed.semanticActions == null) {
+            // The app was using notification actions, and notif wasn't removed yet: keep player
+            if (DEBUG) Log.d(TAG, "Session destroyed but using notification actions $key")
+            mediaEntries.put(key, removed)
+            notifyMediaDataLoaded(key, key, removed)
+        } else if (removed.active) {
+            // This player was still active - it didn't last long enough to time out: remove
+            if (DEBUG) Log.d(TAG, "Removing still-active player $key")
+            notifyMediaDataRemoved(key)
+            logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId)
+        } else {
+            // Convert to resume
+            if (DEBUG) {
+                Log.d(
+                    TAG,
+                    "Notification ($notificationRemoved) and/or session " +
+                        "($hasSession) gone for inactive player $key"
+                )
+            }
+            convertToResumePlayer(removed)
+        }
+    }
+
+    /** Set the given [MediaData] as a resume state player and notify listeners */
+    private fun convertToResumePlayer(data: MediaData) {
+        val key = data.notificationKey!!
+        if (DEBUG) Log.d(TAG, "Converting $key to resume")
+        // Move to resume key (aka package name) if that key doesn't already exist.
+        val resumeAction = data.resumeAction?.let { getResumeMediaAction(it) }
+        val actions = resumeAction?.let { listOf(resumeAction) } ?: emptyList()
+        val launcherIntent =
+            context.packageManager.getLaunchIntentForPackage(data.packageName)?.let {
+                PendingIntent.getActivity(context, 0, it, PendingIntent.FLAG_IMMUTABLE)
+            }
+        val updated =
+            data.copy(
+                token = null,
+                actions = actions,
+                semanticActions = MediaButton(playOrPause = resumeAction),
+                actionsToShowInCompact = listOf(0),
+                active = false,
+                resumption = true,
+                isPlaying = false,
+                isClearable = true,
+                clickIntent = launcherIntent,
+            )
+        val pkg = data.packageName
+        val migrate = mediaEntries.put(pkg, updated) == null
+        // Notify listeners of "new" controls when migrating or removed and update when not
+        Log.d(TAG, "migrating? $migrate from $key -> $pkg")
+        if (migrate) {
+            notifyMediaDataLoaded(key = pkg, oldKey = key, info = updated)
+        } else {
+            // Since packageName is used for the key of the resumption controls, it is
+            // possible that another notification has already been reused for the resumption
+            // controls of this package. In this case, rather than renaming this player as
+            // packageName, just remove it and then send a update to the existing resumption
+            // controls.
+            notifyMediaDataRemoved(key)
+            notifyMediaDataLoaded(key = pkg, oldKey = pkg, info = updated)
+        }
+        logger.logActiveConvertedToResume(updated.appUid, pkg, updated.instanceId)
+    }
+
     fun setMediaResumptionEnabled(isEnabled: Boolean) {
         if (useMediaResumption == isEnabled) {
             return
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
index 7f5c82f..a898b00 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
@@ -71,6 +71,12 @@
      */
     lateinit var stateCallback: (String, PlaybackState) -> Unit
 
+    /**
+     * Callback representing that the [MediaSession] for an active control has been destroyed
+     * @param key Media control unique identifier
+     */
+    lateinit var sessionCallback: (String) -> Unit
+
     init {
         statusBarStateController.addCallback(
             object : StatusBarStateController.StateListener {
@@ -211,6 +217,7 @@
             } else {
                 // For active controls, if the session is destroyed, clean up everything since we
                 // will need to recreate it if this key is updated later
+                sessionCallback.invoke(key)
                 destroy()
             }
         }
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/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 5bc35ca..ab03930 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -45,4 +45,10 @@
 
     /** Check whether we show explicit indicator on UMO */
     fun isExplicitIndicatorEnabled() = featureFlags.isEnabled(Flags.MEDIA_EXPLICIT_INDICATOR)
+
+    /**
+     * If true, keep active media controls for the lifetime of the MediaSession, regardless of
+     * whether the underlying notification was dismissed
+     */
+    fun isRetainingPlayersEnabled() = featureFlags.isEnabled(Flags.MEDIA_RETAIN_SESSIONS)
 }
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/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 1d86343..1edb837 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -24,9 +24,11 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.media.MediaProjectionAppSelectorActivity
 import com.android.systemui.media.MediaProjectionAppSelectorActivity.Companion.EXTRA_HOST_APP_USER_HANDLE
+import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
 import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
 import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
 import com.android.systemui.mediaprojection.appselector.data.IconLoaderLibAppIconLoader
+import com.android.systemui.mediaprojection.appselector.data.RecentTaskLabelLoader
 import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
 import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
 import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider
@@ -43,7 +45,6 @@
 import dagger.Subcomponent
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
-import java.lang.IllegalArgumentException
 import javax.inject.Qualifier
 import javax.inject.Scope
 import kotlinx.coroutines.CoroutineScope
@@ -69,9 +70,10 @@
     ): Activity
 }
 
-/** Scoped values for [MediaProjectionAppSelectorComponent].
- *  We create a scope for the activity so certain dependencies like [TaskPreviewSizeProvider]
- *  could be reused. */
+/**
+ * Scoped values for [MediaProjectionAppSelectorComponent]. We create a scope for the activity so
+ * certain dependencies like [TaskPreviewSizeProvider] could be reused.
+ */
 @Module
 interface MediaProjectionAppSelectorModule {
 
@@ -83,6 +85,10 @@
 
     @Binds
     @MediaProjectionAppSelectorScope
+    fun bindRecentTaskLabelLoader(impl: ActivityTaskManagerLabelLoader): RecentTaskLabelLoader
+
+    @Binds
+    @MediaProjectionAppSelectorScope
     fun bindRecentTaskListProvider(impl: ShellRecentTaskListProvider): RecentTaskListProvider
 
     @Binds
@@ -125,8 +131,10 @@
                 activity.intent.extras
                     ?: error("MediaProjectionAppSelectorActivity should be launched with extras")
             return extras.getParcelable(EXTRA_HOST_APP_USER_HANDLE)
-                ?: error("MediaProjectionAppSelectorActivity should be provided with " +
-                        "$EXTRA_HOST_APP_USER_HANDLE extra")
+                ?: error(
+                    "MediaProjectionAppSelectorActivity should be provided with " +
+                        "$EXTRA_HOST_APP_USER_HANDLE extra"
+                )
         }
 
         @Provides fun bindIconFactory(context: Context): IconFactory = IconFactory.obtain(context)
@@ -146,9 +154,7 @@
     /** Generates [MediaProjectionAppSelectorComponent]. */
     @Subcomponent.Factory
     interface Factory {
-        /**
-         * Create a factory to inject the activity into the graph
-         */
+        /** Create a factory to inject the activity into the graph */
         fun create(
             @BindsInstance activity: MediaProjectionAppSelectorActivity,
             @BindsInstance view: MediaProjectionAppSelectorView,
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskLabelLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskLabelLoader.kt
new file mode 100644
index 0000000..eadcb93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskLabelLoader.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.mediaprojection.appselector.data
+
+import android.annotation.UserIdInt
+import android.content.ComponentName
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+interface RecentTaskLabelLoader {
+    suspend fun loadLabel(userId: Int, componentName: ComponentName): CharSequence?
+}
+
+class ActivityTaskManagerLabelLoader
+@Inject
+constructor(
+    @Background private val coroutineDispatcher: CoroutineDispatcher,
+    private val packageManager: PackageManager
+) : RecentTaskLabelLoader {
+
+    override suspend fun loadLabel(
+        @UserIdInt userId: Int,
+        componentName: ComponentName
+    ): CharSequence? =
+        withContext(coroutineDispatcher) {
+            val userHandle = UserHandle(userId)
+            val appInfo =
+                packageManager.getApplicationInfo(
+                    componentName.packageName,
+                    PackageManager.ApplicationInfoFlags.of(0 /* no flags */)
+                )
+            val label = packageManager.getApplicationLabel(appInfo)
+            return@withContext packageManager.getUserBadgedLabel(label, userHandle)
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
index 15cfeee..64f97f2 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
@@ -20,11 +20,12 @@
 import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
-import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
 import com.android.systemui.R
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelector
 import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
 import com.android.systemui.mediaprojection.appselector.data.RecentTask
+import com.android.systemui.mediaprojection.appselector.data.RecentTaskLabelLoader
 import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
 import dagger.assisted.Assisted
@@ -40,9 +41,10 @@
     @Assisted private val root: ViewGroup,
     private val iconLoader: AppIconLoader,
     private val thumbnailLoader: RecentTaskThumbnailLoader,
+    private val labelLoader: RecentTaskLabelLoader,
     private val taskViewSizeProvider: TaskPreviewSizeProvider,
     @MediaProjectionAppSelector private val scope: CoroutineScope
-) : RecyclerView.ViewHolder(root), ConfigurationListener, TaskPreviewSizeProvider.TaskPreviewSizeListener {
+) : ViewHolder(root), ConfigurationListener, TaskPreviewSizeProvider.TaskPreviewSizeListener {
 
     val thumbnailView: MediaProjectionTaskView = root.requireViewById(R.id.task_thumbnail)
     private val iconView: ImageView = root.requireViewById(R.id.task_icon)
@@ -64,6 +66,10 @@
                         val icon = iconLoader.loadIcon(task.userId, component)
                         iconView.setImageDrawable(icon)
                     }
+                    launch {
+                        val label = labelLoader.loadLabel(task.userId, component)
+                        root.contentDescription = label
+                    }
                 }
                 launch {
                     val thumbnail = thumbnailLoader.loadThumbnail(task.taskId)
@@ -88,10 +94,10 @@
 
     private fun updateThumbnailSize() {
         thumbnailView.layoutParams =
-                thumbnailView.layoutParams.apply {
-                    width = taskViewSizeProvider.size.width()
-                    height = taskViewSizeProvider.size.height()
-                }
+            thumbnailView.layoutParams.apply {
+                width = taskViewSizeProvider.size.width()
+                height = taskViewSizeProvider.size.height()
+            }
     }
 
     @AssistedFactory
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/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 9a65e34..098c617 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -25,12 +25,15 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.AnimationFeatureFlags;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpHandler;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
 import com.android.systemui.plugins.ActivityStarter;
@@ -281,7 +284,8 @@
     static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager,
             KeyguardStateController keyguardStateController,
             Lazy<AlternateBouncerInteractor> alternateBouncerInteractor,
-            InteractionJankMonitor interactionJankMonitor) {
+            InteractionJankMonitor interactionJankMonitor,
+            AnimationFeatureFlags animationFeatureFlags) {
         DialogLaunchAnimator.Callback callback = new DialogLaunchAnimator.Callback() {
             @Override
             public boolean isDreaming() {
@@ -303,6 +307,19 @@
                 return alternateBouncerInteractor.get().canShowAlternateBouncerForFingerprint();
             }
         };
-        return new DialogLaunchAnimator(callback, interactionJankMonitor);
+        return new DialogLaunchAnimator(callback, interactionJankMonitor, animationFeatureFlags);
+    }
+
+    /**
+     */
+    @Provides
+    @SysUISingleton
+    static AnimationFeatureFlags provideAnimationFeatureFlags(FeatureFlags featureFlags) {
+        return new AnimationFeatureFlags() {
+            @Override
+            public boolean isPredictiveBackQsDialogAnim() {
+                return featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM);
+            }
+        };
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 803c282..b642ee6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -100,7 +100,7 @@
 
     private val regionSamplingEnabled =
             featureFlags.isEnabled(Flags.REGION_SAMPLING)
-
+    private var isContentUpdatedOnce = false
     private var showNotifications = false
     private var showSensitiveContentForCurrentUser = false
     private var showSensitiveContentForManagedUser = false
@@ -115,19 +115,6 @@
         override fun onViewAttachedToWindow(v: View) {
             smartspaceViews.add(v as SmartspaceView)
 
-            if (regionSamplingEnabled) {
-                var regionSampler = RegionSampler(
-                        v,
-                        uiExecutor,
-                        bgExecutor,
-                        regionSamplingEnabled,
-                        updateFun
-                )
-                initializeTextColors(regionSampler)
-                regionSampler.startRegionSampler()
-                regionSamplers.put(v, regionSampler)
-            }
-
             connectSession()
 
             updateTextColorFromWallpaper()
@@ -137,12 +124,6 @@
         override fun onViewDetachedFromWindow(v: View) {
             smartspaceViews.remove(v as SmartspaceView)
 
-            if (regionSamplingEnabled) {
-                var regionSampler = regionSamplers.getValue(v)
-                regionSampler.stopRegionSampler()
-                regionSamplers.remove(v)
-            }
-
             if (smartspaceViews.isEmpty()) {
                 disconnect()
             }
@@ -153,6 +134,24 @@
         execution.assertIsMainThread()
         val filteredTargets = targets.filter(::filterSmartspaceTarget)
         plugin?.onTargetsAvailable(filteredTargets)
+        if (!isContentUpdatedOnce) {
+            for (v in smartspaceViews) {
+                if (regionSamplingEnabled) {
+                    var regionSampler = RegionSampler(
+                        v as View,
+                        uiExecutor,
+                        bgExecutor,
+                        regionSamplingEnabled,
+                        updateFun
+                    )
+                    initializeTextColors(regionSampler)
+                    regionSamplers[v] = regionSampler
+                    regionSampler.startRegionSampler()
+                }
+                updateTextColorFromWallpaper()
+            }
+            isContentUpdatedOnce = true
+        }
     }
 
     private val userTrackerCallback = object : UserTracker.Callback {
@@ -399,7 +398,8 @@
 
     private fun updateTextColorFromWallpaper() {
         val wallpaperManager = WallpaperManager.getInstance(context)
-        if (!regionSamplingEnabled || wallpaperManager.lockScreenWallpaperExists()) {
+        if (!regionSamplingEnabled || wallpaperManager.lockScreenWallpaperExists() ||
+            regionSamplers.isEmpty()) {
             val wallpaperTextColor =
                     Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor)
             smartspaceViews.forEach { it.setPrimaryTextColor(wallpaperTextColor) }
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/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index c76b127..00b2fbe 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -51,7 +51,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyFloat
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
@@ -140,8 +139,9 @@
 
     @Test
     fun themeChanged_verifyClockPaletteUpdated() = runBlocking(IMMEDIATE) {
-        verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
-        verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
+        // TODO(b/266103601): delete this test and add more coverage for updateColors()
+        // verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
+        // verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
 
         val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
         verify(configurationController).addCallback(capture(captor))
@@ -152,9 +152,6 @@
 
     @Test
     fun fontChanged_verifyFontSizeUpdated() = runBlocking(IMMEDIATE) {
-        verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
-        verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
-
         val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
         verify(configurationController).addCallback(capture(captor))
         captor.value.onDensityOrFontScaleChanged()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt
new file mode 100644
index 0000000..3bdbf97
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt
@@ -0,0 +1,87 @@
+package com.android.systemui.animation.back
+
+import android.util.DisplayMetrics
+import android.window.BackEvent
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+private data class BackInput(val progressX: Float, val progressY: Float, val edge: Int)
+
+@SmallTest
+@RunWith(JUnit4::class)
+class BackAnimationSpecTest : SysuiTestCase() {
+    private var displayMetrics =
+        DisplayMetrics().apply {
+            widthPixels = 100
+            heightPixels = 200
+            density = 3f
+        }
+
+    @Test
+    fun sysUi_floatingSystemSurfaces_animationValues() {
+        val maxX = 14.0f
+        val maxY = 4.0f
+        val minScale = 0.8f
+
+        val backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi(displayMetrics)
+
+        assertBackTransformation(
+            backAnimationSpec = backAnimationSpec,
+            backInput = BackInput(progressX = 0f, progressY = 0f, edge = BackEvent.EDGE_LEFT),
+            expected = BackTransformation(translateX = 0f, translateY = 0f, scale = 1f),
+        )
+        assertBackTransformation(
+            backAnimationSpec = backAnimationSpec,
+            backInput = BackInput(progressX = 1f, progressY = 0f, edge = BackEvent.EDGE_LEFT),
+            expected = BackTransformation(translateX = -maxX, translateY = 0f, scale = minScale),
+        )
+        assertBackTransformation(
+            backAnimationSpec = backAnimationSpec,
+            backInput = BackInput(progressX = 1f, progressY = 0f, edge = BackEvent.EDGE_RIGHT),
+            expected = BackTransformation(translateX = maxX, translateY = 0f, scale = minScale),
+        )
+        assertBackTransformation(
+            backAnimationSpec = backAnimationSpec,
+            backInput = BackInput(progressX = 1f, progressY = 1f, edge = BackEvent.EDGE_LEFT),
+            expected = BackTransformation(translateX = -maxX, translateY = -maxY, scale = minScale),
+        )
+        assertBackTransformation(
+            backAnimationSpec = backAnimationSpec,
+            backInput = BackInput(progressX = 0f, progressY = 1f, edge = BackEvent.EDGE_LEFT),
+            expected = BackTransformation(translateX = 0f, translateY = -maxY, scale = 1f),
+        )
+        assertBackTransformation(
+            backAnimationSpec = backAnimationSpec,
+            backInput = BackInput(progressX = 0f, progressY = -1f, edge = BackEvent.EDGE_LEFT),
+            expected = BackTransformation(translateX = 0f, translateY = maxY, scale = 1f),
+        )
+    }
+}
+
+private fun assertBackTransformation(
+    backAnimationSpec: BackAnimationSpec,
+    backInput: BackInput,
+    expected: BackTransformation,
+) {
+    val actual = BackTransformation()
+    backAnimationSpec.getBackTransformation(
+        backEvent =
+            BackEvent(
+                /* touchX = */ 0f,
+                /* touchY = */ 0f,
+                /* progress = */ backInput.progressX,
+                /* swipeEdge = */ backInput.edge,
+            ),
+        progressY = backInput.progressY,
+        result = actual
+    )
+
+    val tolerance = 0f
+    assertThat(actual.translateX).isWithin(tolerance).of(expected.translateX)
+    assertThat(actual.translateY).isWithin(tolerance).of(expected.translateY)
+    assertThat(actual.scale).isWithin(tolerance).of(expected.scale)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt
new file mode 100644
index 0000000..190b3d2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt
@@ -0,0 +1,80 @@
+package com.android.systemui.animation.back
+
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+
+@SmallTest
+@RunWith(JUnit4::class)
+class BackTransformationTest : SysuiTestCase() {
+    private val targetView: View = mock()
+
+    @Test
+    fun defaultValue_noTransformation() {
+        val transformation = BackTransformation()
+
+        assertThat(transformation.translateX).isNaN()
+        assertThat(transformation.translateY).isNaN()
+        assertThat(transformation.scale).isNaN()
+    }
+
+    @Test
+    fun applyTo_targetView_translateX_Y_Scale() {
+        val transformation = BackTransformation(translateX = 0f, translateY = 0f, scale = 1f)
+
+        transformation.applyTo(targetView = targetView)
+
+        verify(targetView).translationX = 0f
+        verify(targetView).translationY = 0f
+        verify(targetView).scaleX = 1f
+        verify(targetView).scaleY = 1f
+        verifyNoMoreInteractions(targetView)
+    }
+
+    @Test
+    fun applyTo_targetView_translateX() {
+        val transformation = BackTransformation(translateX = 1f)
+
+        transformation.applyTo(targetView = targetView)
+
+        verify(targetView).translationX = 1f
+        verifyNoMoreInteractions(targetView)
+    }
+
+    @Test
+    fun applyTo_targetView_translateY() {
+        val transformation = BackTransformation(translateY = 2f)
+
+        transformation.applyTo(targetView = targetView)
+
+        verify(targetView).translationY = 2f
+        verifyNoMoreInteractions(targetView)
+    }
+
+    @Test
+    fun applyTo_targetView_scale() {
+        val transformation = BackTransformation(scale = 3f)
+
+        transformation.applyTo(targetView = targetView)
+
+        verify(targetView).scaleX = 3f
+        verify(targetView).scaleY = 3f
+        verifyNoMoreInteractions(targetView)
+    }
+
+    @Test
+    fun applyTo_targetView_noTransformation() {
+        val transformation = BackTransformation()
+
+        transformation.applyTo(targetView = targetView)
+
+        verifyNoMoreInteractions(targetView)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
new file mode 100644
index 0000000..921f9a8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
@@ -0,0 +1,63 @@
+package com.android.systemui.animation.back
+
+import android.util.DisplayMetrics
+import android.window.BackEvent
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(JUnit4::class)
+class OnBackAnimationCallbackExtensionTest : SysuiTestCase() {
+    private val onBackProgress: (BackTransformation) -> Unit = mock()
+    private val onBackStart: (BackEvent) -> Unit = mock()
+    private val onBackInvoke: () -> Unit = mock()
+    private val onBackCancel: () -> Unit = mock()
+
+    private val displayMetrics =
+        DisplayMetrics().apply {
+            widthPixels = 100
+            heightPixels = 100
+            density = 1f
+        }
+
+    private val onBackAnimationCallback =
+        onBackAnimationCallbackFrom(
+            backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi(displayMetrics),
+            displayMetrics = displayMetrics,
+            onBackProgressed = onBackProgress,
+            onBackStarted = onBackStart,
+            onBackInvoked = onBackInvoke,
+            onBackCancelled = onBackCancel,
+        )
+
+    @Test
+    fun onBackProgressed_shouldInvoke_onBackProgress() {
+        val backEvent = BackEvent(0f, 0f, 0f, BackEvent.EDGE_LEFT)
+        onBackAnimationCallback.onBackStarted(backEvent)
+
+        onBackAnimationCallback.onBackProgressed(backEvent)
+
+        verify(onBackProgress).invoke(BackTransformation(0f, 0f, 1f))
+    }
+
+    @Test
+    fun onBackStarted_shouldInvoke_onBackStart() {
+        val backEvent = BackEvent(0f, 0f, 0f, BackEvent.EDGE_LEFT)
+
+        onBackAnimationCallback.onBackStarted(backEvent)
+
+        verify(onBackStart).invoke(backEvent)
+    }
+
+    @Test
+    fun onBackInvoked_shouldInvoke_onBackInvoke() {
+        onBackAnimationCallback.onBackInvoked()
+
+        verify(onBackInvoke).invoke()
+    }
+}
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/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index 1687fdc..1ac6695 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -134,7 +134,8 @@
     private val clock = FakeSystemClock()
     @Mock private lateinit var tunerService: TunerService
     @Captor lateinit var tunableCaptor: ArgumentCaptor<TunerService.Tunable>
-    @Captor lateinit var callbackCaptor: ArgumentCaptor<(String, PlaybackState) -> Unit>
+    @Captor lateinit var stateCallbackCaptor: ArgumentCaptor<(String, PlaybackState) -> Unit>
+    @Captor lateinit var sessionCallbackCaptor: ArgumentCaptor<(String) -> Unit>
     @Captor lateinit var smartSpaceConfigBuilderCaptor: ArgumentCaptor<SmartspaceConfig>
 
     private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20)
@@ -184,6 +185,8 @@
             )
         verify(tunerService)
             .addTunable(capture(tunableCaptor), eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
+        verify(mediaTimeoutListener).stateCallback = capture(stateCallbackCaptor)
+        verify(mediaTimeoutListener).sessionCallback = capture(sessionCallbackCaptor)
         session = MediaSession(context, "MediaDataManagerTestSession")
         mediaNotification =
             SbnBuilder().run {
@@ -230,6 +233,7 @@
         whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L)
         whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(false)
         whenever(mediaFlags.isExplicitIndicatorEnabled()).thenReturn(true)
+        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(false)
         whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId())
     }
 
@@ -547,6 +551,7 @@
         mediaDataManager.onNotificationAdded(KEY_2, mediaNotification)
         assertThat(backgroundExecutor.runAllReady()).isEqualTo(2)
         assertThat(foregroundExecutor.runAllReady()).isEqualTo(2)
+
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -558,9 +563,21 @@
             )
         val data = mediaDataCaptor.value
         assertThat(data.resumption).isFalse()
-        val resumableData = data.copy(resumeAction = Runnable {})
-        mediaDataManager.onMediaDataLoaded(KEY, null, resumableData)
-        mediaDataManager.onMediaDataLoaded(KEY_2, null, resumableData)
+
+        verify(listener)
+            .onMediaDataLoaded(
+                eq(KEY_2),
+                eq(null),
+                capture(mediaDataCaptor),
+                eq(true),
+                eq(0),
+                eq(false)
+            )
+        val data2 = mediaDataCaptor.value
+        assertThat(data2.resumption).isFalse()
+
+        mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
+        mediaDataManager.onMediaDataLoaded(KEY_2, null, data2.copy(resumeAction = Runnable {}))
         reset(listener)
         // WHEN the first is removed
         mediaDataManager.onNotificationRemoved(KEY)
@@ -1310,11 +1327,10 @@
     fun testPlaybackStateChange_keyExists_callsListener() {
         // Notification has been added
         addNotificationAndLoad()
-        verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
 
         // Callback gets an updated state
         val state = PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0L, 1f).build()
-        callbackCaptor.value.invoke(KEY, state)
+        stateCallbackCaptor.value.invoke(KEY, state)
 
         // Listener is notified of updated state
         verify(listener)
@@ -1332,11 +1348,10 @@
     @Test
     fun testPlaybackStateChange_keyDoesNotExist_doesNothing() {
         val state = PlaybackState.Builder().build()
-        verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
 
         // No media added with this key
 
-        callbackCaptor.value.invoke(KEY, state)
+        stateCallbackCaptor.value.invoke(KEY, state)
         verify(listener, never())
             .onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(), anyInt(), anyBoolean())
     }
@@ -1352,10 +1367,9 @@
 
         // And then get a state update
         val state = PlaybackState.Builder().build()
-        verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
 
         // Then no changes are made
-        callbackCaptor.value.invoke(KEY, state)
+        stateCallbackCaptor.value.invoke(KEY, state)
         verify(listener, never())
             .onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(), anyInt(), anyBoolean())
     }
@@ -1367,8 +1381,7 @@
         whenever(controller.playbackState).thenReturn(state)
 
         addNotificationAndLoad()
-        verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
-        callbackCaptor.value.invoke(KEY, state)
+        stateCallbackCaptor.value.invoke(KEY, state)
 
         verify(listener)
             .onMediaDataLoaded(
@@ -1410,8 +1423,7 @@
         backgroundExecutor.runAllReady()
         foregroundExecutor.runAllReady()
 
-        verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
-        callbackCaptor.value.invoke(PACKAGE_NAME, state)
+        stateCallbackCaptor.value.invoke(PACKAGE_NAME, state)
 
         verify(listener)
             .onMediaDataLoaded(
@@ -1436,8 +1448,7 @@
                 .build()
 
         addNotificationAndLoad()
-        verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
-        callbackCaptor.value.invoke(KEY, state)
+        stateCallbackCaptor.value.invoke(KEY, state)
 
         verify(listener)
             .onMediaDataLoaded(
@@ -1485,6 +1496,177 @@
         assertThat(mediaDataCaptor.value.isClearable).isFalse()
     }
 
+    @Test
+    fun testRetain_notifPlayer_notifRemoved_setToResume() {
+        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+
+        // When a media control based on notification is added, times out, and then removed
+        addNotificationAndLoad()
+        mediaDataManager.setTimedOut(KEY, timedOut = true)
+        assertThat(mediaDataCaptor.value.active).isFalse()
+        mediaDataManager.onNotificationRemoved(KEY)
+
+        // It is converted to a resume player
+        verify(listener)
+            .onMediaDataLoaded(
+                eq(PACKAGE_NAME),
+                eq(KEY),
+                capture(mediaDataCaptor),
+                eq(true),
+                eq(0),
+                eq(false)
+            )
+        assertThat(mediaDataCaptor.value.resumption).isTrue()
+        assertThat(mediaDataCaptor.value.active).isFalse()
+        verify(logger)
+            .logActiveConvertedToResume(
+                anyInt(),
+                eq(PACKAGE_NAME),
+                eq(mediaDataCaptor.value.instanceId)
+            )
+    }
+
+    @Test
+    fun testRetain_notifPlayer_sessionDestroyed_doesNotChange() {
+        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+
+        // When a media control based on notification is added and times out
+        addNotificationAndLoad()
+        mediaDataManager.setTimedOut(KEY, timedOut = true)
+        assertThat(mediaDataCaptor.value.active).isFalse()
+
+        // and then the session is destroyed
+        sessionCallbackCaptor.value.invoke(KEY)
+
+        // It remains as a regular player
+        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never())
+            .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+    }
+
+    @Test
+    fun testRetain_notifPlayer_removeWhileActive_fullyRemoved() {
+        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+
+        // When a media control based on notification is added and then removed, without timing out
+        addNotificationAndLoad()
+        val data = mediaDataCaptor.value
+        assertThat(data.active).isTrue()
+        mediaDataManager.onNotificationRemoved(KEY)
+
+        // It is fully removed
+        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
+        verify(listener, never())
+            .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+    }
+
+    @Test
+    fun testRetain_canResume_removeWhileActive_setToResume() {
+        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+
+        // When a media control that supports resumption is added
+        addNotificationAndLoad()
+        val dataResumable = mediaDataCaptor.value.copy(resumeAction = Runnable {})
+        mediaDataManager.onMediaDataLoaded(KEY, null, dataResumable)
+
+        // And then removed while still active
+        mediaDataManager.onNotificationRemoved(KEY)
+
+        // It is converted to a resume player
+        verify(listener)
+            .onMediaDataLoaded(
+                eq(PACKAGE_NAME),
+                eq(KEY),
+                capture(mediaDataCaptor),
+                eq(true),
+                eq(0),
+                eq(false)
+            )
+        assertThat(mediaDataCaptor.value.resumption).isTrue()
+        assertThat(mediaDataCaptor.value.active).isFalse()
+        verify(logger)
+            .logActiveConvertedToResume(
+                anyInt(),
+                eq(PACKAGE_NAME),
+                eq(mediaDataCaptor.value.instanceId)
+            )
+    }
+
+    @Test
+    fun testRetain_sessionPlayer_notifRemoved_doesNotChange() {
+        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        addPlaybackStateAction()
+
+        // When a media control with PlaybackState actions is added, times out,
+        // and then the notification is removed
+        addNotificationAndLoad()
+        val data = mediaDataCaptor.value
+        assertThat(data.active).isTrue()
+        mediaDataManager.setTimedOut(KEY, timedOut = true)
+        mediaDataManager.onNotificationRemoved(KEY)
+
+        // It remains as a regular player
+        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never())
+            .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+    }
+
+    @Test
+    fun testRetain_sessionPlayer_sessionDestroyed_setToResume() {
+        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        addPlaybackStateAction()
+
+        // When a media control with PlaybackState actions is added, times out,
+        // and then the session is destroyed
+        addNotificationAndLoad()
+        val data = mediaDataCaptor.value
+        assertThat(data.active).isTrue()
+        mediaDataManager.setTimedOut(KEY, timedOut = true)
+        sessionCallbackCaptor.value.invoke(KEY)
+
+        // It is converted to a resume player
+        verify(listener)
+            .onMediaDataLoaded(
+                eq(PACKAGE_NAME),
+                eq(KEY),
+                capture(mediaDataCaptor),
+                eq(true),
+                eq(0),
+                eq(false)
+            )
+        assertThat(mediaDataCaptor.value.resumption).isTrue()
+        assertThat(mediaDataCaptor.value.active).isFalse()
+        verify(logger)
+            .logActiveConvertedToResume(
+                anyInt(),
+                eq(PACKAGE_NAME),
+                eq(mediaDataCaptor.value.instanceId)
+            )
+    }
+
+    @Test
+    fun testRetain_sessionPlayer_destroyedWhileActive_fullyRemoved() {
+        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        addPlaybackStateAction()
+
+        // When a media control using session actions is added, and then the session is destroyed
+        // without timing out first
+        addNotificationAndLoad()
+        val data = mediaDataCaptor.value
+        assertThat(data.active).isTrue()
+        sessionCallbackCaptor.value.invoke(KEY)
+
+        // It is fully removed
+        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
+        verify(listener, never())
+            .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+    }
+
     /** Helper function to add a media notification and capture the resulting MediaData */
     private fun addNotificationAndLoad() {
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
@@ -1500,4 +1682,12 @@
                 eq(false)
             )
     }
+
+    /** Helper function to set up a PlaybackState with action */
+    private fun addPlaybackStateAction() {
+        val stateActions = PlaybackState.ACTION_PLAY_PAUSE
+        val stateBuilder = PlaybackState.Builder().setActions(stateActions)
+        stateBuilder.setState(PlaybackState.STATE_PAUSED, 0, 1.0f)
+        whenever(controller.playbackState).thenReturn(stateBuilder.build())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt
index 344dffa..92bf84c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt
@@ -72,6 +72,7 @@
     private lateinit var executor: FakeExecutor
     @Mock private lateinit var timeoutCallback: (String, Boolean) -> Unit
     @Mock private lateinit var stateCallback: (String, PlaybackState) -> Unit
+    @Mock private lateinit var sessionCallback: (String) -> Unit
     @Captor private lateinit var mediaCallbackCaptor: ArgumentCaptor<MediaController.Callback>
     @Captor
     private lateinit var dozingCallbackCaptor:
@@ -99,6 +100,7 @@
             )
         mediaTimeoutListener.timeoutCallback = timeoutCallback
         mediaTimeoutListener.stateCallback = stateCallback
+        mediaTimeoutListener.sessionCallback = sessionCallback
 
         // Create a media session and notification for testing.
         metadataBuilder =
@@ -284,6 +286,7 @@
         verify(mediaController).unregisterCallback(anyObject())
         assertThat(executor.numPending()).isEqualTo(0)
         verify(logger).logSessionDestroyed(eq(KEY))
+        verify(sessionCallback).invoke(eq(KEY))
     }
 
     @Test
@@ -322,6 +325,7 @@
         // THEN the controller is unregistered, but the timeout is still scheduled
         verify(mediaController).unregisterCallback(anyObject())
         assertThat(executor.numPending()).isEqualTo(1)
+        verify(sessionCallback, never()).invoke(eq(KEY))
     }
 
     @Test
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/shared/regionsampling/RegionSamplerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
index 5a62cc1..ae1c8cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
@@ -1,21 +1,17 @@
 package com.android.systemui.shared.regionsampling
 
-import android.graphics.Rect
+import android.app.WallpaperManager
 import android.testing.AndroidTestingRunner
 import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.shared.navigationbar.RegionSamplingHelper
 import java.io.PrintWriter
 import java.util.concurrent.Executor
-import org.junit.Assert
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
 
@@ -28,9 +24,8 @@
     @Mock private lateinit var sampledView: View
     @Mock private lateinit var mainExecutor: Executor
     @Mock private lateinit var bgExecutor: Executor
-    @Mock private lateinit var regionSampler: RegionSamplingHelper
     @Mock private lateinit var pw: PrintWriter
-    @Mock private lateinit var callback: RegionSamplingHelper.SamplingCallback
+    @Mock private lateinit var wallpaperManager: WallpaperManager
 
     private lateinit var mRegionSampler: RegionSampler
     private var updateFun: UpdateColorCallback = {}
@@ -38,65 +33,18 @@
     @Before
     fun setUp() {
         whenever(sampledView.isAttachedToWindow).thenReturn(true)
-        whenever(regionSampler.callback).thenReturn(this@RegionSamplerTest.callback)
 
         mRegionSampler =
-            object : RegionSampler(sampledView, mainExecutor, bgExecutor, true, updateFun) {
-                override fun createRegionSamplingHelper(
-                    sampledView: View,
-                    callback: RegionSamplingHelper.SamplingCallback,
-                    mainExecutor: Executor?,
-                    bgExecutor: Executor?
-                ): RegionSamplingHelper {
-                    return this@RegionSamplerTest.regionSampler
-                }
-            }
+            RegionSampler(sampledView, mainExecutor, bgExecutor, true, updateFun, wallpaperManager)
     }
 
     @Test
     fun testStartRegionSampler() {
         mRegionSampler.startRegionSampler()
-
-        verify(regionSampler).start(Rect(0, 0, 0, 0))
-    }
-
-    @Test
-    fun testStopRegionSampler() {
-        mRegionSampler.stopRegionSampler()
-
-        verify(regionSampler).stop()
     }
 
     @Test
     fun testDump() {
         mRegionSampler.dump(pw)
-
-        verify(regionSampler).dump(pw)
-    }
-
-    @Test
-    fun testUpdateColorCallback() {
-        regionSampler.callback.onRegionDarknessChanged(false)
-        verify(regionSampler.callback).onRegionDarknessChanged(false)
-        clearInvocations(regionSampler.callback)
-        regionSampler.callback.onRegionDarknessChanged(true)
-        verify(regionSampler.callback).onRegionDarknessChanged(true)
-    }
-
-    @Test
-    fun testFlagFalse() {
-        mRegionSampler =
-            object : RegionSampler(sampledView, mainExecutor, bgExecutor, false, updateFun) {
-                override fun createRegionSamplingHelper(
-                    sampledView: View,
-                    callback: RegionSamplingHelper.SamplingCallback,
-                    mainExecutor: Executor?,
-                    bgExecutor: Executor?
-                ): RegionSamplingHelper {
-                    return this@RegionSamplerTest.regionSampler
-                }
-            }
-
-        Assert.assertEquals(mRegionSampler.regionSampler, null)
     }
 }
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/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
index 990db77..f723a9e5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
@@ -23,14 +23,20 @@
     isUnlocked: Boolean = true,
     isShowingAlternateAuthOnUnlock: Boolean = false,
     interactionJankMonitor: InteractionJankMonitor = mock(InteractionJankMonitor::class.java),
+    isPredictiveBackQsDialogAnim: Boolean = false,
 ): DialogLaunchAnimator {
     return DialogLaunchAnimator(
-        FakeCallback(
-            isUnlocked = isUnlocked,
-            isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock,
-        ),
-        interactionJankMonitor,
-        fakeLaunchAnimator(),
+        callback =
+            FakeCallback(
+                isUnlocked = isUnlocked,
+                isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock,
+            ),
+        interactionJankMonitor = interactionJankMonitor,
+        featureFlags =
+            object : AnimationFeatureFlags {
+                override val isPredictiveBackQsDialogAnim = isPredictiveBackQsDialogAnim
+            },
+        launchAnimator = fakeLaunchAnimator(),
         isForTesting = true,
     )
 }
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
index b9823c1..077fd2a 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
@@ -45,10 +45,13 @@
 
     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<>();
 
@@ -58,6 +61,12 @@
         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,
@@ -133,6 +142,7 @@
     @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
@@ -198,8 +208,16 @@
         return mCallingAppIcon;
     }
 
-    public String getReadableCallerId() {
-        return mCallerDisplayName;
+    /**
+     * 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() {
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/Android.bp b/services/core/Android.bp
index 97e9d26..c0bb586 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -156,7 +156,7 @@
         "android.hardware.health-V1.0-java", // HIDL
         "android.hardware.health-V2.0-java", // HIDL
         "android.hardware.health-V2.1-java", // HIDL
-        "android.hardware.health-V1-java", // AIDL
+        "android.hardware.health-V2-java", // AIDL
         "android.hardware.health-translate-java",
         "android.hardware.light-V1-java",
         "android.hardware.tv.cec-V1.1-java",
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/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index fce44f5..faa6938 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -62,6 +62,7 @@
 import android.os.ShellCommand;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
 import android.text.TextUtils;
 import android.util.PackageUtils;
 import android.util.Slog;
@@ -131,6 +132,10 @@
     // used for indicating newly installed MBAs that are updated (but unused currently)
     static final int MBA_STATUS_UPDATED_NEW_INSTALL = 4;
 
+    @VisibleForTesting
+    static final String KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION =
+            "enable_biometric_property_verification";
+
     private static final boolean DEBUG = false;     // toggle this for local debug
 
     private final Context mContext;
@@ -138,6 +143,7 @@
     // the system time (in ms) the last measurement was taken
     private long mMeasurementsLastRecordedMs;
     private PackageManagerInternal mPackageManagerInternal;
+    private BiometricLogger mBiometricLogger;
 
     /**
      * Guards whether or not measurements of MBA to be performed. When this change is enabled,
@@ -1049,13 +1055,56 @@
     }
     private final BinaryTransparencyServiceImpl mServiceImpl;
 
+    /**
+     * A wrapper of {@link FrameworkStatsLog} for easier testing
+     */
+    @VisibleForTesting
+    public static class BiometricLogger {
+        private static final String TAG = "BiometricLogger";
+
+        private static final BiometricLogger sInstance = new BiometricLogger();
+
+        private BiometricLogger() {}
+
+        public static BiometricLogger getInstance() {
+            return sInstance;
+        }
+
+        /**
+         * A wrapper of {@link FrameworkStatsLog}
+         *
+         * @param sensorId The sensorId of the biometric to be logged
+         * @param modality The modality of the biometric
+         * @param sensorType The sensor type of the biometric
+         * @param sensorStrength The sensor strength of the biometric
+         * @param componentId The component Id of a component of the biometric
+         * @param hardwareVersion The hardware version of a component of the biometric
+         * @param firmwareVersion The firmware version of a component of the biometric
+         * @param serialNumber The serial number of a component of the biometric
+         * @param softwareVersion The software version of a component of the biometric
+         */
+        public void logStats(int sensorId, int modality, int sensorType, int sensorStrength,
+                String componentId, String hardwareVersion, String firmwareVersion,
+                String serialNumber, String softwareVersion) {
+            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_PROPERTIES_COLLECTED,
+                    sensorId, modality, sensorType, sensorStrength, componentId, hardwareVersion,
+                    firmwareVersion, serialNumber, softwareVersion);
+        }
+    }
+
     public BinaryTransparencyService(Context context) {
+        this(context, BiometricLogger.getInstance());
+    }
+
+    @VisibleForTesting
+    BinaryTransparencyService(Context context, BiometricLogger biometricLogger) {
         super(context);
         mContext = context;
         mServiceImpl = new BinaryTransparencyServiceImpl();
         mVbmetaDigest = VBMETA_DIGEST_UNINITIALIZED;
         mMeasurementsLastRecordedMs = 0;
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+        mBiometricLogger = biometricLogger;
     }
 
     /**
@@ -1301,7 +1350,7 @@
         // Note: none of the component info is a device identifier since every device of a given
         // model and build share the same biometric system info (see b/216195167)
         for (ComponentInfo componentInfo : prop.getComponentInfo()) {
-            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_PROPERTIES_COLLECTED,
+            mBiometricLogger.logStats(
                     sensorId,
                     modality,
                     sensorType,
@@ -1314,7 +1363,19 @@
         }
     }
 
-    private void collectBiometricProperties() {
+    @VisibleForTesting
+    void collectBiometricProperties() {
+        // Check the flag to determine whether biometric property verification is enabled. It's
+        // disabled by default.
+        if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BIOMETRICS,
+                KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION, false)) {
+            if (DEBUG) {
+                Slog.d(TAG, "Do not collect/verify biometric properties. Feature disabled by "
+                        + "DeviceConfig");
+            }
+            return;
+        }
+
         PackageManager pm = mContext.getPackageManager();
         FingerprintManager fpManager = null;
         FaceManager faceManager = null;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 4e5ce88..cfd22e8 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,6 +3993,66 @@
         }
     }
 
+    /**
+     * 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/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 70f07a9..523e0f0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -951,7 +951,7 @@
             "short_fgs_timeout_duration";
 
     /** @see #KEY_SHORT_FGS_TIMEOUT_DURATION */
-    static final long DEFAULT_SHORT_FGS_TIMEOUT_DURATION = 60_000;
+    static final long DEFAULT_SHORT_FGS_TIMEOUT_DURATION = 3 * 60_000;
 
     /** @see #KEY_SHORT_FGS_TIMEOUT_DURATION */
     public volatile long mShortFgsTimeoutDuration = DEFAULT_SHORT_FGS_TIMEOUT_DURATION;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index de87a0c..7d1d0e5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8432,15 +8432,13 @@
                 t.traceEnd();
             }
 
+            boolean isBootingSystemUser = currentUserId == UserHandle.USER_SYSTEM;
+
             // Some systems - like automotive - will explicitly unlock system user then switch
-            // to a secondary user. Hence, we don't want to send duplicate broadcasts for
-            // the system user here.
+            // to a secondary user.
             // TODO(b/242195409): this workaround shouldn't be necessary once we move
             // the headless-user start logic to UserManager-land.
-            final boolean isBootingSystemUser = (currentUserId == UserHandle.USER_SYSTEM)
-                    && !UserManager.isHeadlessSystemUserMode();
-
-            if (isBootingSystemUser) {
+            if (isBootingSystemUser && !UserManager.isHeadlessSystemUserMode()) {
                 t.traceBegin("startHomeOnAllDisplays");
                 mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
                 t.traceEnd();
@@ -8452,6 +8450,10 @@
 
 
             if (isBootingSystemUser) {
+                // Need to send the broadcasts for the system user here because
+                // UserController#startUserInternal will not send them for the system user starting,
+                // It checks if the user state already exists, which is always the case for the
+                // system user.
                 t.traceBegin("sendUserStartBroadcast");
                 final int callingUid = Binder.getCallingUid();
                 final int callingPid = Binder.getCallingPid();
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/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 81655cf..114e2c1 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -143,6 +143,7 @@
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * All of the code required to compute proc states and oom_adj values.
@@ -743,6 +744,45 @@
                 queue.offer(provider);
                 provider.mState.setReachable(true);
             }
+            // See if this process has any corresponding SDK sandbox processes running, and if so
+            // scan them as well.
+            final List<ProcessRecord> sdkSandboxes =
+                    mProcessList.getSdkSandboxProcessesForAppLocked(pr.uid);
+            final int numSdkSandboxes = sdkSandboxes != null ? sdkSandboxes.size() : 0;
+            for (int i = numSdkSandboxes - 1; i >= 0; i--) {
+                ProcessRecord sdkSandbox = sdkSandboxes.get(i);
+                containsCycle |= sdkSandbox.mState.isReachable();
+                if (sdkSandbox.mState.isReachable()) {
+                    continue;
+                }
+                queue.offer(sdkSandbox);
+                sdkSandbox.mState.setReachable(true);
+            }
+            // If this process is a sandbox itself, also scan the app on whose behalf its running
+            if (pr.isSdkSandbox) {
+                for (int is = psr.numberOfRunningServices() - 1; is >= 0; is--) {
+                    ServiceRecord s = psr.getRunningServiceAt(is);
+                    ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
+                            s.getConnections();
+                    for (int conni = serviceConnections.size() - 1; conni >= 0; conni--) {
+                        ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni);
+                        for (int i = clist.size() - 1; i >= 0; i--) {
+                            ConnectionRecord cr = clist.get(i);
+                            ProcessRecord attributedApp = cr.binding.attributedClient;
+                            if (attributedApp == null || attributedApp == pr
+                                    || ((attributedApp.mState.getMaxAdj() >= ProcessList.SYSTEM_ADJ)
+                                    && (attributedApp.mState.getMaxAdj() < FOREGROUND_APP_ADJ))) {
+                                continue;
+                            }
+                            if (attributedApp.mState.isReachable()) {
+                                continue;
+                            }
+                            queue.offer(attributedApp);
+                            attributedApp.mState.setReachable(true);
+                        }
+                    }
+                }
+            }
         }
 
         int size = processes.size();
@@ -2131,6 +2171,11 @@
                     boolean trackedProcState = false;
 
                     ProcessRecord client = cr.binding.client;
+                    if (app.isSdkSandbox && cr.binding.attributedClient != null) {
+                        // For SDK sandboxes, use the attributed client (eg the app that
+                        // requested the sandbox)
+                        client = cr.binding.attributedClient;
+                    }
                     final ProcessStateRecord cstate = client.mState;
                     if (computeClients) {
                         computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now,
@@ -2377,12 +2422,12 @@
                             state.setAdjType(adjType);
                             state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
                                     .REASON_SERVICE_IN_USE);
-                            state.setAdjSource(cr.binding.client);
+                            state.setAdjSource(client);
                             state.setAdjSourceProcState(clientProcState);
                             state.setAdjTarget(s.instanceName);
                             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
-                                        + ": " + app + ", due to " + cr.binding.client
+                                        + ": " + app + ", due to " + client
                                         + " adj=" + adj + " procState="
                                         + ProcessList.makeProcStateString(procState));
                             }
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/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3a40fc7..04c0d64 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -485,6 +485,12 @@
     final ProcessMap<AppZygote> mAppZygotes = new ProcessMap<AppZygote>();
 
     /**
+     * The currently running SDK sandbox processes for a uid.
+     */
+    @GuardedBy("mService")
+    final SparseArray<ArrayList<ProcessRecord>> mSdkSandboxes = new SparseArray<>();
+
+    /**
      * Managees the {@link android.app.ApplicationExitInfo} records.
      */
     @GuardedBy("mAppExitInfoTracker")
@@ -2990,6 +2996,14 @@
         if (proc.isolated) {
             mIsolatedProcesses.put(proc.uid, proc);
         }
+        if (proc.isSdkSandbox) {
+            ArrayList<ProcessRecord> sdkSandboxes = mSdkSandboxes.get(proc.uid);
+            if (sdkSandboxes == null) {
+                sdkSandboxes = new ArrayList<>();
+            }
+            sdkSandboxes.add(proc);
+            mSdkSandboxes.put(Process.getAppUidForSdkSandboxUid(proc.uid), sdkSandboxes);
+        }
     }
 
     @GuardedBy("mService")
@@ -3030,6 +3044,19 @@
         return ret;
     }
 
+    /**
+     * Returns the associated SDK sandbox processes for a UID. Note that this does
+     * NOT return a copy, so callers should not modify the result, or use it outside
+     * of the lock scope.
+     *
+     * @param uid UID to return sansdbox processes for
+     */
+    @Nullable
+    @GuardedBy("mService")
+    List<ProcessRecord> getSdkSandboxProcessesForAppLocked(int uid) {
+        return mSdkSandboxes.get(uid);
+    }
+
     @GuardedBy("mService")
     ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
             boolean isolated, int isolatedUid, boolean isSdkSandbox, int sdkSandboxUid,
@@ -3135,6 +3162,16 @@
         if (record != null && record.appZygote) {
             removeProcessFromAppZygoteLocked(record);
         }
+        if (record != null && record.isSdkSandbox) {
+            final int appUid = Process.getAppUidForSdkSandboxUid(uid);
+            final ArrayList<ProcessRecord> sdkSandboxesForUid = mSdkSandboxes.get(appUid);
+            if (sdkSandboxesForUid != null) {
+                sdkSandboxesForUid.remove(record);
+                if (sdkSandboxesForUid.size() == 0) {
+                    mSdkSandboxes.remove(appUid);
+                }
+            }
+        }
         mAppsInBackgroundRestricted.remove(record);
 
         return old;
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/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index d195103..893c8b5 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -40,6 +40,7 @@
 import android.app.GameState;
 import android.app.IGameManagerService;
 import android.app.IGameModeListener;
+import android.app.IUidObserver;
 import android.app.StatsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -165,38 +166,19 @@
     private final ArrayMap<IGameModeListener, Integer> mGameModeListeners = new ArrayMap<>();
     @Nullable
     private final GameServiceController mGameServiceController;
+    private final Object mUidObserverLock = new Object();
+    @VisibleForTesting
+    @Nullable
+    final UidObserver mUidObserver;
+    @GuardedBy("mUidObserverLock")
+    private final Set<Integer> mForegroundGameUids = new HashSet<>();
 
     public GameManagerService(Context context) {
         this(context, createServiceThread().getLooper());
     }
 
     GameManagerService(Context context, Looper looper) {
-        mContext = context;
-        mHandler = new SettingsHandler(looper);
-        mPackageManager = mContext.getPackageManager();
-        mUserManager = mContext.getSystemService(UserManager.class);
-        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
-        mSystemDir = new File(Environment.getDataDirectory(), "system");
-        mSystemDir.mkdirs();
-        FileUtils.setPermissions(mSystemDir.toString(),
-                FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IROTH | FileUtils.S_IXOTH,
-                -1, -1);
-        mGameModeInterventionListFile = new AtomicFile(new File(mSystemDir,
-                                                     GAME_MODE_INTERVENTION_LIST_FILE_NAME));
-        FileUtils.setPermissions(mGameModeInterventionListFile.getBaseFile().getAbsolutePath(),
-                FileUtils.S_IRUSR | FileUtils.S_IWUSR
-                        | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
-                -1, -1);
-        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_GAME_SERVICE)) {
-            mGameServiceController = new GameServiceController(
-                    context, BackgroundThread.getExecutor(),
-                    new GameServiceProviderSelectorImpl(
-                            context.getResources(),
-                            context.getPackageManager()),
-                    new GameServiceProviderInstanceFactoryImpl(context));
-        } else {
-            mGameServiceController = null;
-        }
+        this(context, looper, Environment.getDataDirectory());
     }
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -227,6 +209,14 @@
         } else {
             mGameServiceController = null;
         }
+        mUidObserver = new UidObserver();
+        try {
+            ActivityManager.getService().registerUidObserver(mUidObserver,
+                    ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
+                    ActivityManager.PROCESS_STATE_UNKNOWN, null);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not register UidObserver");
+        }
     }
 
     @Override
@@ -2152,4 +2142,66 @@
      * load dynamic library for frame rate overriding JNI calls
      */
     private static native void nativeSetOverrideFrameRate(int uid, float frameRate);
+
+    final class UidObserver extends IUidObserver.Stub {
+        @Override
+        public void onUidIdle(int uid, boolean disabled) {}
+
+        @Override
+        public void onUidGone(int uid, boolean disabled) {
+            synchronized (mUidObserverLock) {
+                disableGameMode(uid);
+            }
+        }
+
+        @Override
+        public void onUidActive(int uid) {}
+
+        @Override
+        public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
+            synchronized (mUidObserverLock) {
+                if (ActivityManager.isProcStateBackground(procState)) {
+                    disableGameMode(uid);
+                    return;
+                }
+
+                final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+                if (packages == null || packages.length == 0) {
+                    return;
+                }
+
+                final int userId = mContext.getUserId();
+                if (!Arrays.stream(packages).anyMatch(p -> isPackageGame(p, userId))) {
+                    return;
+                }
+
+                if (mForegroundGameUids.isEmpty()) {
+                    Slog.v(TAG, "Game power mode ON (process state was changed to foreground)");
+                    mPowerManagerInternal.setPowerMode(Mode.GAME, true);
+                }
+                mForegroundGameUids.add(uid);
+            }
+        }
+
+        private void disableGameMode(int uid) {
+            synchronized (mUidObserverLock) {
+                if (!mForegroundGameUids.contains(uid)) {
+                    return;
+                }
+                mForegroundGameUids.remove(uid);
+                if (!mForegroundGameUids.isEmpty()) {
+                    return;
+                }
+                Slog.v(TAG,
+                        "Game power mode OFF (process remomved or state changed to background)");
+                mPowerManagerInternal.setPowerMode(Mode.GAME, false);
+            }
+        }
+
+        @Override
+        public void onUidCachedChanged(int uid, boolean cached) {}
+
+        @Override
+        public void onUidProcAdjChanged(int uid) {}
+    }
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 92a9f46..5c44cea 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1265,6 +1265,20 @@
                 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
     }
 
+    private void initVolumeStreamStates() {
+        int numStreamTypes = AudioSystem.getNumStreamTypes();
+        synchronized (VolumeStreamState.class) {
+            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
+                VolumeStreamState streamState = mStreamStates[streamType];
+                int groupId = getVolumeGroupForStreamType(streamType);
+                if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP
+                        && sVolumeGroupStates.indexOfKey(groupId) >= 0) {
+                    streamState.setVolumeGroupState(sVolumeGroupStates.get(groupId));
+                }
+            }
+        }
+    }
+
     /**
      * Separating notification volume from ring is NOT of aliasing the corresponding streams
      * @param properties
@@ -1292,6 +1306,8 @@
         initVolumeGroupStates();
 
         mSoundDoseHelper.initSafeUsbMediaVolumeIndex();
+        // Link VGS on VSS
+        initVolumeStreamStates();
 
         // Call setRingerModeInt() to apply correct mute
         // state on streams affected by ringer mode.
@@ -2610,13 +2626,11 @@
         }
         if (!TextUtils.isEmpty(packageName)) {
             PackageManager pm = mContext.getPackageManager();
-            ActivityManager am =
-                          (ActivityManager) mContext.getSystemService(mContext.ACTIVITY_SERVICE);
 
             if (pm.checkPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD, packageName)
                     == PackageManager.PERMISSION_GRANTED) {
                 try {
-                    assistantUid = pm.getPackageUidAsUser(packageName, am.getCurrentUser());
+                    assistantUid = pm.getPackageUidAsUser(packageName, getCurrentUserId());
                 } catch (PackageManager.NameNotFoundException e) {
                     Log.e(TAG,
                             "updateAssistantUId() could not find UID for package: " + packageName);
@@ -3482,15 +3496,7 @@
                 } else {
                     state = direction == AudioManager.ADJUST_MUTE;
                 }
-                for (int stream = 0; stream < mStreamStates.length; stream++) {
-                    if (streamTypeAlias == mStreamVolumeAlias[stream]) {
-                        if (!(readCameraSoundForced()
-                                    && (mStreamStates[stream].getStreamType()
-                                        == AudioSystem.STREAM_SYSTEM_ENFORCED))) {
-                            mStreamStates[stream].mute(state);
-                        }
-                    }
-                }
+                muteAliasStreams(streamTypeAlias, state);
             } else if ((direction == AudioManager.ADJUST_RAISE)
                     && mSoundDoseHelper.raiseVolumeDisplaySafeMediaVolume(streamTypeAlias,
                             aliasIndex + step, device, flags)) {
@@ -3505,7 +3511,7 @@
                     // Unmute the stream if it was previously muted
                     if (direction == AudioManager.ADJUST_RAISE) {
                         // unmute immediately for volume up
-                        streamState.mute(false);
+                        muteAliasStreams(streamTypeAlias, false);
                     } else if (direction == AudioManager.ADJUST_LOWER) {
                         if (mIsSingleVolume) {
                             sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE,
@@ -3631,6 +3637,42 @@
         sendVolumeUpdate(streamType, oldIndex, newIndex, flags, device);
     }
 
+    /**
+     * Loops on aliasted stream, update the mute cache attribute of each
+     * {@see AudioService#VolumeStreamState}, and then apply the change.
+     * It prevents to unnecessary {@see AudioSystem#setStreamVolume} done for each stream
+     * and aliases before mute change changed and after.
+     */
+    private void muteAliasStreams(int streamAlias, boolean state) {
+        synchronized (VolumeStreamState.class) {
+            List<Integer> streamsToMute = new ArrayList<>();
+            for (int stream = 0; stream < mStreamStates.length; stream++) {
+                if (streamAlias == mStreamVolumeAlias[stream]) {
+                    if (!(readCameraSoundForced()
+                            && (mStreamStates[stream].getStreamType()
+                                    == AudioSystem.STREAM_SYSTEM_ENFORCED))) {
+                        boolean changed = mStreamStates[stream].mute(state, /* apply= */ false);
+                        if (changed) {
+                            streamsToMute.add(stream);
+                        }
+                    }
+                }
+            }
+            streamsToMute.forEach(streamToMute -> {
+                mStreamStates[streamToMute].doMute();
+                broadcastMuteSetting(streamToMute, state);
+            });
+        }
+    }
+
+    private void broadcastMuteSetting(int streamType, boolean isMuted) {
+        // Stream mute changed, fire the intent.
+        Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
+        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
+        intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, isMuted);
+        sendBroadcastToAll(intent, null /* options */);
+    }
+
     // Called after a delay when volume down is pressed while muted
     private void onUnmuteStream(int stream, int flags) {
         boolean wasMuted;
@@ -3730,7 +3772,8 @@
         // except for BT SCO stream where only explicit mute is allowed to comply to BT requirements
         if ((streamType != AudioSystem.STREAM_BLUETOOTH_SCO)
                 && (getDeviceForStream(stream) == device)) {
-            mStreamStates[stream].mute(index == 0);
+            // As adjustStreamVolume with muteAdjust flags mute/unmutes stream and aliased streams.
+            muteAliasStreams(stream, index == 0);
         }
     }
 
@@ -3774,23 +3817,23 @@
     }
 
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-    /** @see AudioManager#setVolumeIndexForAttributes(attr, int, int) */
-    public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags,
+    @Override
+    @android.annotation.EnforcePermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING
+    })
+    /** @see AudioManager#setVolumeGroupVolumeIndex(int, int, int) */
+    public void setVolumeGroupVolumeIndex(int groupId, int index, int flags,
             String callingPackage, String attributionTag) {
-        super.setVolumeIndexForAttributes_enforcePermission();
-
-        Objects.requireNonNull(attr, "attr must not be null");
-        int volumeGroup = AudioProductStrategy.getVolumeGroupIdForAudioAttributes(
-                attr, /* fallbackOnDefault= */false);
-        if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
-            Log.e(TAG, ": no volume group found for attributes " + attr.toString());
+        super.setVolumeGroupVolumeIndex_enforcePermission();
+        if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+            Log.e(TAG, ": no volume group found for id " + groupId);
             return;
         }
-        VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
+        VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
 
-        sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, attr, vgs.name(),
-                index, flags, callingPackage + ", user " + ActivityManager.getCurrentUser()));
+        sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, vgs.name(),
+                index, flags, callingPackage + ", user " + getCurrentUserId()));
 
         vgs.setVolumeIndex(index, flags);
 
@@ -3799,7 +3842,7 @@
             try {
                 ensureValidStreamType(groupedStream);
             } catch (IllegalArgumentException e) {
-                Log.d(TAG, "volume group " + volumeGroup + " has internal streams (" + groupedStream
+                Log.d(TAG, "volume group " + groupId + " has internal streams (" + groupedStream
                         + "), do not change associated stream volume");
                 continue;
             }
@@ -3821,39 +3864,55 @@
         return null;
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-    /** @see AudioManager#getVolumeIndexForAttributes(attr) */
-    public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
-        super.getVolumeIndexForAttributes_enforcePermission();
-
-        Objects.requireNonNull(attr, "attr must not be null");
+    @Override
+    @android.annotation.EnforcePermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING
+    })
+    /** @see AudioManager#getVolumeGroupVolumeIndex(int) */
+    public int getVolumeGroupVolumeIndex(int groupId) {
+        super.getVolumeGroupVolumeIndex_enforcePermission();
         synchronized (VolumeStreamState.class) {
-            int volumeGroup = AudioProductStrategy.getVolumeGroupIdForAudioAttributes(
-                    attr, /* fallbackOnDefault= */false);
-            if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
-                throw new IllegalArgumentException("No volume group for attributes " + attr);
+            if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+                throw new IllegalArgumentException("No volume group for id " + groupId);
             }
-            VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
-            return vgs.isMuted() ? vgs.getMinIndex() : vgs.getVolumeIndex();
+            VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+            // Return 0 when muted, not min index since for e.g. Voice Call, it has a non zero
+            // min but it mutable on permission condition.
+            return vgs.isMuted() ? 0 : vgs.getVolumeIndex();
         }
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-    /** @see AudioManager#getMaxVolumeIndexForAttributes(attr) */
-    public int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
-        super.getMaxVolumeIndexForAttributes_enforcePermission();
-
-        Objects.requireNonNull(attr, "attr must not be null");
-        return AudioSystem.getMaxVolumeIndexForAttributes(attr);
+    /** @see AudioManager#getVolumeGroupMaxVolumeIndex(int) */
+    @android.annotation.EnforcePermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING
+    })
+    public int getVolumeGroupMaxVolumeIndex(int groupId) {
+        super.getVolumeGroupMaxVolumeIndex_enforcePermission();
+        synchronized (VolumeStreamState.class) {
+            if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+                throw new IllegalArgumentException("No volume group for id " + groupId);
+            }
+            VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+            return vgs.getMaxIndex();
+        }
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-    /** @see AudioManager#getMinVolumeIndexForAttributes(attr) */
-    public int getMinVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
-        super.getMinVolumeIndexForAttributes_enforcePermission();
-
-        Objects.requireNonNull(attr, "attr must not be null");
-        return AudioSystem.getMinVolumeIndexForAttributes(attr);
+    /** @see AudioManager#getVolumeGroupMinVolumeIndex(int) */
+    @android.annotation.EnforcePermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING
+    })
+    public int getVolumeGroupMinVolumeIndex(int groupId) {
+        super.getVolumeGroupMinVolumeIndex_enforcePermission();
+        synchronized (VolumeStreamState.class) {
+            if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+                throw new IllegalArgumentException("No volume group for id " + groupId);
+            }
+            VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+            return vgs.getMinIndex();
+        }
     }
 
     @Override
@@ -3922,6 +3981,71 @@
                 callingPackage, /*attributionTag*/ null);
     }
 
+    /** @see AudioManager#adjustVolumeGroupVolume(int, int, int) */
+    public void adjustVolumeGroupVolume(int groupId, int direction, int flags,
+                                        String callingPackage) {
+        ensureValidDirection(direction);
+        if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+            Log.e(TAG, ": no volume group found for id " + groupId);
+            return;
+        }
+        VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+        // For compatibility reason, use stream API if group linked to a valid stream
+        boolean fallbackOnStream = false;
+        for (int stream : vgs.getLegacyStreamTypes()) {
+            try {
+                ensureValidStreamType(stream);
+            } catch (IllegalArgumentException e) {
+                Log.d(TAG, "volume group " + groupId + " has internal streams (" + stream
+                        + "), do not change associated stream volume");
+                continue;
+            }
+            // Note: Group and Stream does not share same convention, 0 is mute for stream,
+            // min index is acting as mute for Groups
+            if (vgs.isVssMuteBijective(stream)) {
+                adjustStreamVolume(stream, direction, flags, callingPackage);
+                if (isMuteAdjust(direction)) {
+                    // will be propagated to all aliased streams
+                    return;
+                }
+                fallbackOnStream = true;
+            }
+        }
+        if (fallbackOnStream) {
+            // Handled by at least one stream, will be propagated to group, bailing out.
+            return;
+        }
+        sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_ADJUST_GROUP_VOL, vgs.name(),
+                direction, flags, callingPackage));
+        vgs.adjustVolume(direction, flags);
+    }
+
+    /** @see AudioManager#getLastAudibleVolumeGroupVolume(int) */
+    @android.annotation.EnforcePermission(android.Manifest.permission.QUERY_AUDIO_STATE)
+    public int getLastAudibleVolumeGroupVolume(int groupId) {
+        super.getLastAudibleVolumeGroupVolume_enforcePermission();
+        synchronized (VolumeStreamState.class) {
+            if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+                Log.e(TAG, ": no volume group found for id " + groupId);
+                return 0;
+            }
+            VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+            return vgs.getVolumeIndex();
+        }
+    }
+
+    /** @see AudioManager#isVolumeGroupMuted(int) */
+    public boolean isVolumeGroupMuted(int groupId) {
+        synchronized (VolumeStreamState.class) {
+            if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
+                Log.e(TAG, ": no volume group found for id " + groupId);
+                return false;
+            }
+            VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
+            return vgs.isMuted();
+        }
+    }
+
     /** @see AudioManager#setStreamVolume(int, int, int)
      * Part of service interface, check permissions here */
     public void setStreamVolumeWithAttribution(int streamType, int index, int flags,
@@ -5110,7 +5234,7 @@
     }
 
     private void setRingerMode(int ringerMode, String caller, boolean external) {
-        if (mUseFixedVolume || mIsSingleVolume) {
+        if (mUseFixedVolume || mIsSingleVolume || mUseVolumeGroupAliases) {
             return;
         }
         if (caller == null || caller.length() == 0) {
@@ -7413,6 +7537,16 @@
                 || stream == AudioSystem.STREAM_BLUETOOTH_SCO;
     }
 
+    private static int getVolumeGroupForStreamType(int stream) {
+        AudioAttributes attributes =
+                AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(stream);
+        if (attributes.equals(new AudioAttributes.Builder().build())) {
+            return AudioVolumeGroup.DEFAULT_VOLUME_GROUP;
+        }
+        return AudioProductStrategy.getVolumeGroupIdForAudioAttributes(
+                attributes, /* fallbackOnDefault= */ false);
+    }
+
     // NOTE: Locking order for synchronized objects related to volume management:
     //  1     mSettingsLock
     //  2       VolumeStreamState.class
@@ -7521,14 +7655,62 @@
             return mIsMuted;
         }
 
+        public void adjustVolume(int direction, int flags) {
+            synchronized (AudioService.VolumeStreamState.class) {
+                int device = getDeviceForVolume();
+                int previousIndex = getIndex(device);
+                if (isMuteAdjust(direction) && !isMutable()) {
+                    // Non mutable volume group
+                    if (DEBUG_VOL) {
+                        Log.d(TAG, "invalid mute on unmutable volume group " + name());
+                    }
+                    return;
+                }
+                switch (direction) {
+                    case AudioManager.ADJUST_TOGGLE_MUTE: {
+                        // Note: If muted by volume 0, unmute will restore volume 0.
+                        mute(!mIsMuted);
+                        break;
+                    }
+                    case AudioManager.ADJUST_UNMUTE:
+                        // Note: If muted by volume 0, unmute will restore volume 0.
+                        mute(false);
+                        break;
+                    case AudioManager.ADJUST_MUTE:
+                        // May be already muted by setvolume 0, prevent from setting same value
+                        if (previousIndex != 0) {
+                            // bypass persist
+                            mute(true);
+                        }
+                        mIsMuted = true;
+                        break;
+                    case AudioManager.ADJUST_RAISE:
+                        // As for stream, RAISE during mute will increment the index
+                        setVolumeIndex(Math.min(previousIndex + 1, mIndexMax),  device, flags);
+                        break;
+                    case AudioManager.ADJUST_LOWER:
+                        // For stream, ADJUST_LOWER on a muted VSS is a no-op
+                        // If we decide to unmute on ADJUST_LOWER, cannot fallback on
+                        // adjustStreamVolume for group associated to legacy stream type
+                        if (isMuted() && previousIndex != 0) {
+                            mute(false);
+                        } else {
+                            int newIndex = Math.max(previousIndex - 1, mIndexMin);
+                            setVolumeIndex(newIndex, device, flags);
+                        }
+                        break;
+                }
+            }
+        }
+
         public int getVolumeIndex() {
-            synchronized (VolumeStreamState.class) {
+            synchronized (AudioService.VolumeStreamState.class) {
                 return getIndex(getDeviceForVolume());
             }
         }
 
         public void setVolumeIndex(int index, int flags) {
-            synchronized (VolumeStreamState.class) {
+            synchronized (AudioService.VolumeStreamState.class) {
                 if (mUseFixedVolume) {
                     return;
                 }
@@ -7613,7 +7795,7 @@
 
         public void applyAllVolumes(boolean userSwitch) {
             String caller = "from vgs";
-            synchronized (VolumeStreamState.class) {
+            synchronized (AudioService.VolumeStreamState.class) {
                 // apply device specific volumes first
                 for (int i = 0; i < mIndexMap.size(); i++) {
                     int device = mIndexMap.keyAt(i);
@@ -7713,7 +7895,7 @@
                 Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group "
                         + mAudioVolumeGroup.name()
                         + ", device " + AudioSystem.getOutputDeviceName(device)
-                        + " and User=" + ActivityManager.getCurrentUser());
+                        + " and User=" + getCurrentUserId());
             }
             boolean success = mSettings.putSystemIntForUser(mContentResolver,
                     getSettingNameForDevice(device),
@@ -7725,7 +7907,7 @@
         }
 
         public void readSettings() {
-            synchronized (VolumeStreamState.class) {
+            synchronized (AudioService.VolumeStreamState.class) {
                 // force maximum volume on all streams if fixed volume property is set
                 if (mUseFixedVolume) {
                     mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
@@ -7752,7 +7934,7 @@
                     if (DEBUG_VOL) {
                         Log.v(TAG, "readSettings: found stored index " + getValidIndex(index)
                                  + " for group " + mAudioVolumeGroup.name() + ", device: " + name
-                                 + ", User=" + ActivityManager.getCurrentUser());
+                                 + ", User=" + getCurrentUserId());
                     }
                     mIndexMap.put(device, getValidIndex(index));
                 }
@@ -7829,13 +8011,14 @@
     //  4       VolumeStreamState.class
     /*package*/ class VolumeStreamState {
         private final int mStreamType;
+        private VolumeGroupState mVolumeGroupState = null;
         private int mIndexMin;
         // min index when user doesn't have permission to change audio settings
         private int mIndexMinNoPerm;
         private int mIndexMax;
 
-        private boolean mIsMuted;
-        private boolean mIsMutedInternally;
+        private boolean mIsMuted = false;
+        private boolean mIsMutedInternally = false;
         private String mVolumeIndexSettingName;
         @NonNull private Set<Integer> mObservedDeviceSet = new TreeSet<>();
 
@@ -7908,6 +8091,15 @@
         }
 
         /**
+         * Associate a {@link volumeGroupState} on the {@link VolumeStreamState}.
+         * <p> It helps to synchronize the index, mute attributes on the maching
+         * {@link volumeGroupState}
+         * @param volumeGroupState matching the {@link VolumeStreamState}
+         */
+        public void setVolumeGroupState(VolumeGroupState volumeGroupState) {
+            mVolumeGroupState = volumeGroupState;
+        }
+        /**
          * Update the minimum index that can be used without MODIFY_AUDIO_SETTINGS permission
          * @param index minimum index expressed in "UI units", i.e. no 10x factor
          */
@@ -8168,6 +8360,9 @@
                 }
             }
             if (changed) {
+                // If associated to volume group, update group cache
+                updateVolumeGroupIndex(device, /* forceMuteState= */ false);
+
                 oldIndex = (oldIndex + 5) / 10;
                 index = (index + 5) / 10;
                 // log base stream changes to the event log
@@ -8285,6 +8480,28 @@
             }
         }
 
+        // If associated to volume group, update group cache
+        private void updateVolumeGroupIndex(int device, boolean forceMuteState) {
+            synchronized (VolumeStreamState.class) {
+                if (mVolumeGroupState != null) {
+                    int groupIndex = (getIndex(device) + 5) / 10;
+                    if (DEBUG_VOL) {
+                        Log.d(TAG, "updateVolumeGroupIndex for stream " + mStreamType
+                                + ", muted=" + mIsMuted + ", device=" + device + ", index="
+                                + getIndex(device) + ", group " + mVolumeGroupState.name()
+                                + " Muted=" + mVolumeGroupState.isMuted() + ", Index=" + groupIndex
+                                + ", forceMuteState=" + forceMuteState);
+                    }
+                    mVolumeGroupState.updateVolumeIndex(groupIndex, device);
+                    // Only propage mute of stream when applicable
+                    if (mIndexMin == 0 || isCallStream(mStreamType)) {
+                        // For call stream, align mute only when muted, not when index is set to 0
+                        mVolumeGroupState.mute(forceMuteState ? mIsMuted : groupIndex == 0);
+                    }
+                }
+            }
+        }
+
         /**
          * Mute/unmute the stream
          * @param state the new mute state
@@ -8293,27 +8510,10 @@
         public boolean mute(boolean state) {
             boolean changed = false;
             synchronized (VolumeStreamState.class) {
-                if (state != mIsMuted) {
-                    changed = true;
-                    mIsMuted = state;
-
-                    // Set the new mute volume. This propagates the values to
-                    // the audio system, otherwise the volume won't be changed
-                    // at the lower level.
-                    sendMsg(mAudioHandler,
-                            MSG_SET_ALL_VOLUMES,
-                            SENDMSG_QUEUE,
-                            0,
-                            0,
-                            this, 0);
-                }
+                changed = mute(state, true);
             }
             if (changed) {
-                // Stream mute changed, fire the intent.
-                Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
-                intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
-                intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state);
-                sendBroadcastToAll(intent, null /* options */);
+                broadcastMuteSetting(mStreamType, state);
             }
             return changed;
         }
@@ -8345,6 +8545,44 @@
             return mIsMuted || mIsMutedInternally;
         }
 
+        /**
+         * Mute/unmute the stream
+         * @param state the new mute state
+         * @param apply true to propagate to HW, or false just to update the cache. May be needed
+         * to mute a stream and its aliases as applyAllVolume will force settings to aliases.
+         * It prevents unnecessary calls to {@see AudioSystem#setStreamVolume}
+         * @return true if the mute state was changed
+         */
+        public boolean mute(boolean state, boolean apply) {
+            synchronized (VolumeStreamState.class) {
+                boolean changed = state != mIsMuted;
+                if (changed) {
+                    mIsMuted = state;
+                    if (apply) {
+                        doMute();
+                    }
+                }
+                return changed;
+            }
+        }
+
+        public void doMute() {
+            synchronized (VolumeStreamState.class) {
+                // If associated to volume group, update group cache
+                updateVolumeGroupIndex(getDeviceForStream(mStreamType), /* forceMuteState= */ true);
+
+                // Set the new mute volume. This propagates the values to
+                // the audio system, otherwise the volume won't be changed
+                // at the lower level.
+                sendMsg(mAudioHandler,
+                        MSG_SET_ALL_VOLUMES,
+                        SENDMSG_QUEUE,
+                        0,
+                        0,
+                        this, 0);
+            }
+        }
+
         public int getStreamType() {
             return mStreamType;
         }
@@ -8414,6 +8652,9 @@
             pw.println();
             pw.print("   Devices: ");
             pw.print(AudioSystem.deviceSetToString(getDeviceSetForStream(mStreamType)));
+            pw.println();
+            pw.print("   Volume Group: ");
+            pw.println(mVolumeGroupState != null ? mVolumeGroupState.name() : "n/a");
         }
     }
 
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index d30bec7..58caf5a 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -17,7 +17,6 @@
 package com.android.server.audio;
 
 import android.annotation.NonNull;
-import android.media.AudioAttributes;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioManager;
 import android.media.AudioSystem;
@@ -197,6 +196,7 @@
         static final int VOL_SET_GROUP_VOL = 8;
         static final int VOL_MUTE_STREAM_INT = 9;
         static final int VOL_SET_LE_AUDIO_VOL = 10;
+        static final int VOL_ADJUST_GROUP_VOL = 11;
 
         final int mOp;
         final int mStream;
@@ -204,7 +204,6 @@
         final int mVal2;
         final String mCaller;
         final String mGroupName;
-        final AudioAttributes mAudioAttributes;
 
         /** used for VOL_ADJUST_VOL_UID,
          *           VOL_ADJUST_SUGG_VOL,
@@ -217,7 +216,6 @@
             mVal2 = val2;
             mCaller = caller;
             mGroupName = null;
-            mAudioAttributes = null;
             logMetricEvent();
         }
 
@@ -230,7 +228,6 @@
             mStream = -1;
             mCaller = null;
             mGroupName = null;
-            mAudioAttributes = null;
             logMetricEvent();
         }
 
@@ -243,7 +240,6 @@
             mStream = -1;
             mCaller = null;
             mGroupName = null;
-            mAudioAttributes = null;
             logMetricEvent();
         }
 
@@ -256,7 +252,6 @@
             // unused
             mCaller = null;
             mGroupName = null;
-            mAudioAttributes = null;
             logMetricEvent();
         }
 
@@ -269,19 +264,18 @@
             // unused
             mCaller = null;
             mGroupName = null;
-            mAudioAttributes = null;
             logMetricEvent();
         }
 
-        /** used for VOL_SET_GROUP_VOL */
-        VolumeEvent(int op, AudioAttributes aa, String group, int index, int flags, String caller) {
+        /** used for VOL_SET_GROUP_VOL,
+         *           VOL_ADJUST_GROUP_VOL */
+        VolumeEvent(int op, String group, int index, int flags, String caller) {
             mOp = op;
             mStream = -1;
             mVal1 = index;
             mVal2 = flags;
             mCaller = caller;
             mGroupName = group;
-            mAudioAttributes = aa;
             logMetricEvent();
         }
 
@@ -293,7 +287,6 @@
             mVal2 = 0;
             mCaller = null;
             mGroupName = null;
-            mAudioAttributes = null;
             logMetricEvent();
         }
 
@@ -335,6 +328,15 @@
                             .record();
                     return;
                 }
+                case VOL_ADJUST_GROUP_VOL:
+                    new MediaMetrics.Item(mMetricsId)
+                            .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller)
+                            .set(MediaMetrics.Property.DIRECTION, mVal1 > 0 ? "up" : "down")
+                            .set(MediaMetrics.Property.EVENT, "adjustVolumeGroupVolume")
+                            .set(MediaMetrics.Property.FLAGS, mVal2)
+                            .set(MediaMetrics.Property.GROUP, mGroupName)
+                            .record();
+                    return;
                 case VOL_SET_STREAM_VOL:
                     new MediaMetrics.Item(mMetricsId)
                             .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller)
@@ -386,7 +388,6 @@
                     return;
                 case VOL_SET_GROUP_VOL:
                     new MediaMetrics.Item(mMetricsId)
-                            .set(MediaMetrics.Property.ATTRIBUTES, mAudioAttributes.toString())
                             .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller)
                             .set(MediaMetrics.Property.EVENT, "setVolumeIndexForAttributes")
                             .set(MediaMetrics.Property.FLAGS, mVal2)
@@ -412,6 +413,13 @@
                             .append(" flags:0x").append(Integer.toHexString(mVal2))
                             .append(") from ").append(mCaller)
                             .toString();
+                case VOL_ADJUST_GROUP_VOL:
+                    return new StringBuilder("adjustVolumeGroupVolume(group:")
+                            .append(mGroupName)
+                            .append(" dir:").append(AudioManager.adjustToString(mVal1))
+                            .append(" flags:0x").append(Integer.toHexString(mVal2))
+                            .append(") from ").append(mCaller)
+                            .toString();
                 case VOL_ADJUST_STREAM_VOL:
                     return new StringBuilder("adjustStreamVolume(stream:")
                             .append(AudioSystem.streamToString(mStream))
@@ -460,8 +468,7 @@
                             .append(" stream:").append(AudioSystem.streamToString(mStream))
                             .toString();
                 case VOL_SET_GROUP_VOL:
-                    return new StringBuilder("setVolumeIndexForAttributes(attr:")
-                            .append(mAudioAttributes.toString())
+                    return new StringBuilder("setVolumeIndexForAttributes(group:")
                             .append(" group: ").append(mGroupName)
                             .append(" index:").append(mVal1)
                             .append(" flags:0x").append(Integer.toHexString(mVal2))
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/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 df0e2d3..ba9fe38 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -327,6 +327,7 @@
     private float[] mNitsRange;
 
     private final HighBrightnessModeController mHbmController;
+    private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;
 
     private final BrightnessThrottler mBrightnessThrottler;
 
@@ -415,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();
@@ -431,6 +432,7 @@
         mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
                 mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
                 () -> updatePowerState(), mDisplayId, mSensorManager);
+        mHighBrightnessModeMetadata = hbmMetadata;
         mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController);
         mTag = "DisplayPowerController2[" + mDisplayId + "]";
 
@@ -681,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: "
@@ -695,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) {
@@ -703,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
@@ -750,7 +753,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();
@@ -762,6 +766,7 @@
                     mBrightnessRampIncreaseMaxTimeMillis,
                     mBrightnessRampDecreaseMaxTimeMillis);
         }
+        mHbmController.setHighBrightnessModeMetadata(hbmMetadata);
         mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
                 mDisplayDeviceConfig.getHighBrightnessModeData(),
                 new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -1718,7 +1723,7 @@
                     if (mAutomaticBrightnessController != null) {
                         mAutomaticBrightnessController.update();
                     }
-                }, mContext);
+                }, mHighBrightnessModeMetadata, mContext);
     }
 
     private BrightnessThrottler createBrightnessThrottlerLocked() {
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/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index ceb8785..96faee2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -1096,15 +1096,15 @@
                 HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.length];
                 int i = 0;
                 for (android.hardware.tv.hdmi.connection.HdmiPortInfo portInfo : hdmiPortInfos) {
-                    hdmiPortInfo[i] =
-                            new HdmiPortInfo(
+                    hdmiPortInfo[i] = new HdmiPortInfo.Builder(
                                     portInfo.portId,
                                     portInfo.type,
-                                    portInfo.physicalAddress,
-                                    portInfo.cecSupported,
-                                    false,
-                                    portInfo.arcSupported,
-                                    false);
+                                    portInfo.physicalAddress)
+                                    .setCecSupported(portInfo.cecSupported)
+                                    .setMhlSupported(false)
+                                    .setArcSupported(portInfo.arcSupported)
+                                    .setEarcSupported(false)
+                                    .build();
                     i++;
                 }
                 return hdmiPortInfo;
@@ -1260,13 +1260,15 @@
                 HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.size()];
                 int i = 0;
                 for (android.hardware.tv.cec.V1_0.HdmiPortInfo portInfo : hdmiPortInfos) {
-                    hdmiPortInfo[i] = new HdmiPortInfo(portInfo.portId,
+                    hdmiPortInfo[i] = new HdmiPortInfo.Builder(
+                            portInfo.portId,
                             portInfo.type,
-                            portInfo.physicalAddress,
-                            portInfo.cecSupported,
-                            false,
-                            portInfo.arcSupported,
-                            false);
+                            portInfo.physicalAddress)
+                            .setCecSupported(portInfo.cecSupported)
+                            .setMhlSupported(false)
+                            .setArcSupported(portInfo.arcSupported)
+                            .setEarcSupported(false)
+                            .build();
                     i++;
                 }
                 return hdmiPortInfo;
@@ -1442,13 +1444,15 @@
                 HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.size()];
                 int i = 0;
                 for (android.hardware.tv.cec.V1_0.HdmiPortInfo portInfo : hdmiPortInfos) {
-                    hdmiPortInfo[i] = new HdmiPortInfo(portInfo.portId,
+                    hdmiPortInfo[i] = new HdmiPortInfo.Builder(
+                            portInfo.portId,
                             portInfo.type,
-                            portInfo.physicalAddress,
-                            portInfo.cecSupported,
-                            false,
-                            portInfo.arcSupported,
-                            false);
+                            portInfo.physicalAddress)
+                            .setCecSupported(portInfo.cecSupported)
+                            .setMhlSupported(false)
+                            .setArcSupported(portInfo.arcSupported)
+                            .setEarcSupported(false)
+                            .build();
                     i++;
                 }
                 return hdmiPortInfo;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index a57292a..18a69c8 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -469,9 +469,12 @@
         ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
         for (HdmiPortInfo info : cecPortInfo) {
             if (mhlSupportedPorts.contains(info.getId())) {
-                result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
-                        info.isCecSupported(), true, info.isArcSupported(),
-                        info.isEarcSupported()));
+                result.add(new HdmiPortInfo.Builder(info.getId(), info.getType(), info.getAddress())
+                        .setCecSupported(info.isCecSupported())
+                        .setMhlSupported(true)
+                        .setArcSupported(info.isArcSupported())
+                        .setEarcSupported(info.isEarcSupported())
+                        .build());
             } else {
                 result.add(info);
             }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 31b8ef2..48e0c30 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -114,6 +114,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
+import android.util.FeatureFlagUtils;
 import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -2503,6 +2504,28 @@
         return mRecoverableKeyStoreManager.getKey(alias);
     }
 
+    /**
+     * Starts a session to verify lock screen credentials provided by a remote device.
+     */
+    public void startRemoteLockscreenValidation() {
+        if (!FeatureFlagUtils.isEnabled(mContext,
+                FeatureFlagUtils.SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API)) {
+            throw new UnsupportedOperationException("Under development");
+        }
+        mRecoverableKeyStoreManager.startRemoteLockscreenValidation();
+    }
+
+    /**
+     * Verifies credentials guess from a remote device.
+     */
+    public void validateRemoteLockscreen(@NonNull byte[] encryptedCredential) {
+        if (!FeatureFlagUtils.isEnabled(mContext,
+                FeatureFlagUtils.SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API)) {
+            throw new UnsupportedOperationException("Under development");
+        }
+        mRecoverableKeyStoreManager.validateRemoteLockscreen(encryptedCredential);
+    }
+
     // Reading these settings needs the contacts permission
     private static final String[] READ_CONTACTS_PROTECTED_SETTINGS = new String[] {
             Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index e620c80..7c80d8a 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -965,6 +965,35 @@
         }
     }
 
+    /**
+     * Starts a session to verify lock screen credentials provided by a remote device.
+     */
+    public void startRemoteLockscreenValidation() {
+        checkVerifyRemoteLockscreenPermission();
+        // TODO(b/254335492): Create session in memory
+        return;
+    }
+
+    /**
+     * Verifies encrypted credentials guess from a remote device.
+     */
+    public void validateRemoteLockscreen(@NonNull byte[] encryptedCredential) {
+        checkVerifyRemoteLockscreenPermission();
+        // TODO(b/254335492): Decrypt and verify credentials
+        return;
+    }
+
+    private void checkVerifyRemoteLockscreenPermission() {
+        // TODO(b/254335492): Check new system permission
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.RECOVER_KEYSTORE,
+                "Caller " + Binder.getCallingUid()
+                        + " doesn't have verifyRemoteLockscreen permission.");
+        int userId = UserHandle.getCallingUserId();
+        int uid = Binder.getCallingUid();
+        mCleanupManager.registerRecoveryAgent(userId, uid);
+    }
+
     private void checkRecoverKeyStorePermission() {
         mContext.enforceCallingOrSelfPermission(
                 Manifest.permission.RECOVER_KEYSTORE,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java
new file mode 100644
index 0000000..7698c28
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java
@@ -0,0 +1,89 @@
+/*
+ * 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.locksettings.recoverablekeystore.storage;
+
+import android.annotation.Nullable;
+import android.util.SparseArray;
+
+import java.security.KeyPair;
+
+/**
+ * Memory based storage for keyPair used to send encrypted credentials from a remote device.
+ *
+ * @hide
+ */
+public class RemoteLockscreenValidationSessionStorage {
+
+    private final SparseArray<LockScreenVerificationSession> mSessionsByUserId =
+            new SparseArray<>();
+
+    /**
+     * Returns session for given user or null.
+     *
+     * @param userId The user id
+     * @return The session info.
+     *
+     * @hide
+     */
+    @Nullable
+    public LockScreenVerificationSession get(int userId) {
+        return mSessionsByUserId.get(userId);
+    }
+
+    /**
+     * Creates a new session to verify lockscreen credentials guess.
+     *
+     * Session will be automatically removed after 10 minutes of inactivity.
+     * @param userId The user id
+     *
+     * @hide
+     */
+    public LockScreenVerificationSession startSession(int userId) {
+        if (mSessionsByUserId.get(userId) != null) {
+            mSessionsByUserId.remove(userId);
+        }
+        LockScreenVerificationSession newSession = null;
+        // TODO(b/254335492): Schedule a task to remove session.
+        mSessionsByUserId.put(userId, newSession);
+        return newSession;
+    }
+
+    /**
+     * Deletes session for a user.
+     */
+    public void remove(int userId) {
+        mSessionsByUserId.remove(userId);
+    }
+
+    /**
+     * Holder for keypair used by remote lock screen validation.
+     *
+     * @hide
+     */
+    public static class LockScreenVerificationSession {
+        private final KeyPair mKeyPair;
+        private final long mSessionStartTimeMillis;
+
+        /**
+         * @hide
+         */
+        public LockScreenVerificationSession(KeyPair keyPair, long sessionStartTimeMillis) {
+            mKeyPair = keyPair;
+            mSessionStartTimeMillis = sessionStartTimeMillis;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 3ad0e44..a9b9b54 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -69,6 +69,7 @@
 import com.android.server.LocalServices;
 import com.android.server.Watchdog;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -247,6 +248,24 @@
 
     // Binder call
     @Override
+    public void showMediaOutputSwitcher(String packageName) {
+        if (!validatePackageName(Binder.getCallingUid(), packageName)) {
+            throw new SecurityException("packageName must match the calling identity");
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                StatusBarManagerInternal statusBar =
+                        LocalServices.getService(StatusBarManagerInternal.class);
+                statusBar.showMediaOutputSwitcher(packageName);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // Binder call
+    @Override
     public MediaRouterClientState getState(IMediaRouterClient client) {
         final long token = Binder.clearCallingIdentity();
         try {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 3df46a2..a7e0af3 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -774,6 +774,21 @@
                 if (!deleteAllUsers) {
                     returnCode = deletePackageX(internalPackageName, versionCode,
                             userId, deleteFlags, false /*removedBySystem*/);
+
+                    // Get a list of child user profiles and delete if package is
+                    // present in clone profile.
+                    int[] childUserIds = mUserManagerInternal.getProfileIds(userId, true);
+                    for (int childId : childUserIds) {
+                        if (childId != userId) {
+                            UserInfo userInfo = mUserManagerInternal.getUserInfo(childId);
+                            if (userInfo != null && userInfo.isCloneProfile()) {
+                                returnCode = deletePackageX(internalPackageName, versionCode,
+                                        childId, deleteFlags, false /*removedBySystem*/);
+                                break;
+                            }
+                        }
+                    }
+
                 } else {
                     int[] blockUninstallUserIds = getBlockUninstallForUsers(innerSnapshot,
                             internalPackageName, users);
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/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 94fb840..ca5fa5f 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -67,6 +67,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -91,6 +92,7 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 
 /**
  * This interface supplies all UI-specific behavior of the window manager.  An
@@ -357,6 +359,11 @@
          *                      windows even if the rotation hasn't changed.
          */
         void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout);
+
+        /**
+         * Invoked when a screenshot is taken of the given display to notify registered listeners.
+         */
+        List<ComponentName> notifyScreenshotListeners(int displayId);
     }
 
     /**
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/soundtrigger_middleware/OWNERS b/services/core/java/com/android/server/soundtrigger_middleware/OWNERS
index e5d0370..01b2cb9 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/OWNERS
+++ b/services/core/java/com/android/server/soundtrigger_middleware/OWNERS
@@ -1,2 +1,2 @@
-ytai@google.com
+atneya@google.com
 elaurent@google.com
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/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 73b2238..29b37ce 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2184,10 +2184,10 @@
                 return null;
             }
 
-            final long identity = Binder.clearCallingIdentity();
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
                     userId, "acquireTvInputHardware");
+            final long identity = Binder.clearCallingIdentity();
             try {
                 return mTvInputHardwareManager.acquireHardware(
                         deviceId, callback, info, callingUid, resolvedUserId,
@@ -2205,10 +2205,10 @@
                 return;
             }
 
-            final long identity = Binder.clearCallingIdentity();
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
                     userId, "releaseTvInputHardware");
+            final long identity = Binder.clearCallingIdentity();
             try {
                 mTvInputHardwareManager.releaseHardware(
                         deviceId, hardware, callingUid, resolvedUserId);
@@ -2350,10 +2350,10 @@
                 throws RemoteException {
             ensureCaptureTvInputPermission();
 
-            final long identity = Binder.clearCallingIdentity();
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
                     userId, "getAvailableTvStreamConfigList");
+            final long identity = Binder.clearCallingIdentity();
             try {
                 return mTvInputHardwareManager.getAvailableTvStreamConfigList(
                         inputId, callingUid, resolvedUserId);
@@ -2368,10 +2368,10 @@
                 throws RemoteException {
             ensureCaptureTvInputPermission();
 
-            final long identity = Binder.clearCallingIdentity();
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
                     userId, "captureFrame");
+            final long identity = Binder.clearCallingIdentity();
             try {
                 String hardwareInputId = null;
                 synchronized (mLock) {
@@ -2400,10 +2400,10 @@
         @Override
         public boolean isSingleSessionActive(int userId) throws RemoteException {
             ensureCaptureTvInputPermission();
-            final long identity = Binder.clearCallingIdentity();
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
                     userId, "isSingleSessionActive");
+            final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
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/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 90ac1aa..fd3f32d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -249,6 +249,7 @@
 import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityOptions;
 import android.app.ICompatCameraControlCallback;
+import android.app.IScreenCaptureObserver;
 import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
 import android.app.ResultInfo;
@@ -301,6 +302,7 @@
 import android.os.LocaleList;
 import android.os.PersistableBundle;
 import android.os.Process;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
@@ -886,6 +888,8 @@
 
     private AppSaturationInfo mLastAppSaturationInfo;
 
+    private RemoteCallbackList<IScreenCaptureObserver> mCaptureCallbacks;
+
     private final ColorDisplayService.ColorTransformController mColorTransformController =
             (matrix, translation) -> mWmService.mH.post(() -> {
                 synchronized (mWmService.mGlobalLock) {
@@ -7020,6 +7024,41 @@
         return mLocusId;
     }
 
+    public void reportScreenCaptured() {
+        if (mCaptureCallbacks != null) {
+            final int n = mCaptureCallbacks.beginBroadcast();
+            for (int i = 0; i < n; i++) {
+                IScreenCaptureObserver obs = mCaptureCallbacks.getBroadcastItem(i);
+                try {
+                    obs.onScreenCaptured();
+                } catch (RemoteException e) {
+                }
+            }
+            mCaptureCallbacks.finishBroadcast();
+        }
+    }
+
+    public void registerCaptureObserver(IScreenCaptureObserver observer) {
+        synchronized (mWmService.mGlobalLock) {
+            if (mCaptureCallbacks == null) {
+                mCaptureCallbacks = new RemoteCallbackList<IScreenCaptureObserver>();
+            }
+            mCaptureCallbacks.register(observer);
+        }
+    }
+
+    public void unregisterCaptureObserver(IScreenCaptureObserver observer) {
+        synchronized (mWmService.mGlobalLock) {
+            if (mCaptureCallbacks != null) {
+                mCaptureCallbacks.unregister(observer);
+            }
+        }
+    }
+
+    boolean isRegisteredForScreenCaptureCallback() {
+        return mCaptureCallbacks != null && mCaptureCallbacks.getRegisteredCallbackCount() > 0;
+    }
+
     void setVoiceSessionLocked(IVoiceInteractionSession session) {
         voiceSession = session;
         pendingVoiceInteractionStart = false;
diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
index 64af9dd..47e78f0 100644
--- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
+++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
@@ -72,7 +72,7 @@
     }
 
     @GuardedBy("ActivityTaskManagerService.mGlobalLock")
-    static boolean shouldBlockActivityStart(int uid) {
+    static boolean shouldRestrictActivitySwitch(int uid) {
         return flagEnabledForUid(sAsmRestrictionsEnabled, uid);
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d88f719..d6d3dc7 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1972,7 +1972,7 @@
         );
 
         boolean shouldBlockActivityStart =
-                ActivitySecurityModelFeatureFlags.shouldBlockActivityStart(mCallingUid);
+                ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(mCallingUid);
 
         if (ActivitySecurityModelFeatureFlags.shouldShowToast(mCallingUid)) {
             UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
@@ -2080,6 +2080,7 @@
                 reusedTask != null ? reusedTask.getTopNonFinishingActivity() : null, intentGrants);
 
         if (mAddingToTask) {
+            clearTopIfNeeded(targetTask, mCallingUid, mStartActivity.getUid(), mLaunchFlags);
             return START_SUCCESS;
         }
 
@@ -2112,6 +2113,55 @@
     }
 
     /**
+     * If the top activity uid does not match the launched activity, and the launch was not
+     * requested from the top uid, we want to clear out all non matching activities to prevent the
+     * top activity being sandwiched.
+     */
+    private void clearTopIfNeeded(@NonNull Task targetTask, int callingUid, int startingUid,
+            int launchFlags) {
+        if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != FLAG_ACTIVITY_NEW_TASK) {
+            // Launch is from the same task, so must be a top or privileged UID
+            return;
+        }
+
+        ActivityRecord targetTaskTop = targetTask.getTopNonFinishingActivity();
+        if (targetTaskTop != null && targetTaskTop.getUid() != startingUid) {
+            boolean shouldBlockActivityStart = ActivitySecurityModelFeatureFlags
+                    .shouldRestrictActivitySwitch(callingUid);
+            int[] finishCount = new int[0];
+            if (shouldBlockActivityStart) {
+                ActivityRecord activity = targetTask.getActivity(
+                        ar -> !ar.finishing && ar.isUid(startingUid));
+
+                if (activity == null) {
+                    // mStartActivity is not in task, so clear everything
+                    activity = mStartActivity;
+                }
+
+                finishCount = new int[1];
+                if (activity != null) {
+                    targetTask.performClearTop(activity, launchFlags, finishCount);
+                }
+
+                if (finishCount[0] > 0) {
+                    Slog.w(TAG, "Clearing top n: " + finishCount[0] + " activities from task t: "
+                            + targetTask + " not matching top uid: " + callingUid);
+                }
+            }
+
+            if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)
+                    && (!shouldBlockActivityStart || finishCount[0] > 0)) {
+                UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
+                        (shouldBlockActivityStart
+                                ? "Top activities cleared by "
+                                : "Top activities would be cleared by ")
+                                + ActivitySecurityModelFeatureFlags.DOC_LINK,
+                        Toast.LENGTH_SHORT).show());
+            }
+        }
+    }
+
+    /**
      * Check if the activity being launched is the same as the one currently at the top and it
      * should only be launched once.
      */
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a927ed3..2f82167 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.BIND_VOICE_INTERACTION;
 import static android.Manifest.permission.CHANGE_CONFIGURATION;
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.DETECT_SCREEN_CAPTURE;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
@@ -145,6 +146,7 @@
 import android.app.IApplicationThread;
 import android.app.IAssistDataReceiver;
 import android.app.INotificationManager;
+import android.app.IScreenCaptureObserver;
 import android.app.ITaskStackListener;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -5437,6 +5439,32 @@
         }
     }
 
+    @Override
+    public void registerScreenCaptureObserver(IBinder activityToken,
+            IScreenCaptureObserver observer) {
+        mAmInternal.enforceCallingPermission(DETECT_SCREEN_CAPTURE,
+                "registerScreenCaptureObserver");
+        synchronized (mGlobalLock) {
+            ActivityRecord activityRecord = ActivityRecord.forTokenLocked(activityToken);
+            if (activityRecord != null) {
+                activityRecord.registerCaptureObserver(observer);
+            }
+        }
+    }
+
+    @Override
+    public void unregisterScreenCaptureObserver(IBinder activityToken,
+            IScreenCaptureObserver observer) {
+        mAmInternal.enforceCallingPermission(DETECT_SCREEN_CAPTURE,
+                "unregisterScreenCaptureObserver");
+        synchronized (mGlobalLock) {
+            ActivityRecord activityRecord = ActivityRecord.forTokenLocked(activityToken);
+            if (activityRecord != null) {
+                activityRecord.unregisterCaptureObserver(observer);
+            }
+        }
+    }
+
     /**
      * Returns {@code true} if the process represented by the pid passed as argument is
      * instrumented and the instrumentation source was granted with the permission also
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 8149e1c..0f1f51f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -137,6 +137,7 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.view.Display;
+import android.widget.Toast;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -147,6 +148,7 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
+import com.android.server.UiThread;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.HostingRecord;
 import com.android.server.am.UserState;
@@ -1628,16 +1630,16 @@
             // Prevent recursion.
             return;
         }
+        boolean passesAsmChecks = true;
         // We may have already checked that the callingUid has additional clearTask privileges, and
         // cleared the calling identify. If so, we infer we do not need further restrictions here.
         // TODO(b/263368846) Move to live with the rest of the ASM logic.
         if (callingUid != SYSTEM_UID) {
-            boolean passesAsmChecks = doesTopActivityMatchingUidExistForAsm(task, callingUid,
+            passesAsmChecks = doesTopActivityMatchingUidExistForAsm(task, callingUid,
                     null);
             if (!passesAsmChecks) {
                 ActivityRecord topActivity =  task.getActivity(ar ->
                         !ar.isState(FINISHING) && !ar.isAlwaysOnTop());
-                Slog.i(TAG, "Finishing task from background. t: " + task);
                 FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
                         /* caller_uid */
                         callingUid,
@@ -1676,6 +1678,28 @@
             if (task.isPersistable) {
                 mService.notifyTaskPersisterLocked(null, true);
             }
+            if (!passesAsmChecks) {
+                boolean shouldRestrictActivitySwitch =
+                        ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(callingUid);
+
+                if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)) {
+                    UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
+                            (shouldRestrictActivitySwitch
+                                    ? "Returning home due to "
+                                    : "Would return home due to ")
+                                    + ActivitySecurityModelFeatureFlags.DOC_LINK,
+                            Toast.LENGTH_SHORT).show());
+                }
+
+                // If the activity switch should be restricted, return home rather than the
+                // previously top task, to prevent users from being confused which app they're
+                // viewing
+                if (shouldRestrictActivitySwitch) {
+                    Slog.w(TAG, "Return to home as source uid: " + callingUid
+                            + "is not on top of task t: " + task);
+                    task.getTaskDisplayArea().moveHomeActivityToTop("taskRemoved");
+                }
+            }
         } finally {
             task.mInRemoveTask = false;
         }
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/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 67e188f..d2f62da 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -724,6 +724,7 @@
      *   <li>Activity is portrait-only.
      *   <li>Fullscreen window in landscape device orientation.
      *   <li>Horizontal Reachability is enabled.
+     *   <li>Activity fills parent vertically.
      * </ul>
      */
     private boolean isHorizontalReachabilityEnabled(Configuration parentConfiguration) {
@@ -731,10 +732,14 @@
                 && parentConfiguration.windowConfiguration.getWindowingMode()
                         == WINDOWING_MODE_FULLSCREEN
                 && (parentConfiguration.orientation == ORIENTATION_LANDSCAPE
-                && mActivityRecord.getOrientationForReachability() == ORIENTATION_PORTRAIT);
+                        && mActivityRecord.getOrientationForReachability() == ORIENTATION_PORTRAIT)
+                // Check whether the activity fills the parent vertically.
+                && parentConfiguration.windowConfiguration.getBounds().height()
+                        == mActivityRecord.getBounds().height();
     }
 
-    private boolean isHorizontalReachabilityEnabled() {
+    @VisibleForTesting
+    boolean isHorizontalReachabilityEnabled() {
         return isHorizontalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
     }
 
@@ -746,6 +751,7 @@
      *   <li>Activity is landscape-only.
      *   <li>Fullscreen window in portrait device orientation.
      *   <li>Vertical Reachability is enabled.
+     *   <li>Activity fills parent horizontally.
      * </ul>
      */
     private boolean isVerticalReachabilityEnabled(Configuration parentConfiguration) {
@@ -753,10 +759,14 @@
                 && parentConfiguration.windowConfiguration.getWindowingMode()
                         == WINDOWING_MODE_FULLSCREEN
                 && (parentConfiguration.orientation == ORIENTATION_PORTRAIT
-                && mActivityRecord.getOrientationForReachability() == ORIENTATION_LANDSCAPE);
+                        && mActivityRecord.getOrientationForReachability() == ORIENTATION_LANDSCAPE)
+                // Check whether the activity fills the parent horizontally.
+                && parentConfiguration.windowConfiguration.getBounds().width()
+                        == mActivityRecord.getBounds().width();
     }
 
-    private boolean isVerticalReachabilityEnabled() {
+    @VisibleForTesting
+    boolean isVerticalReachabilityEnabled() {
         return isVerticalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
     }
 
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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index bded45e..cb1b3e4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -26,6 +26,7 @@
 import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
 import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.Manifest.permission.STATUS_BAR_SERVICE;
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
@@ -168,6 +169,7 @@
 import android.app.IAssistDataReceiver;
 import android.app.WindowConfiguration;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -9447,4 +9449,55 @@
     public void markSurfaceSyncGroupReady(IBinder syncGroupToken) {
         mSurfaceSyncGroupController.markSyncGroupReady(syncGroupToken);
     }
+
+    private ArraySet<ActivityRecord> getVisibleActivityRecords(int displayId) {
+        ArraySet<ActivityRecord> result = new ArraySet<>();
+        synchronized (mGlobalLock) {
+            ArraySet<ComponentName> addedActivities = new ArraySet<>();
+            DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent != null) {
+                displayContent.forAllWindows(
+                        (w) -> {
+                            if (w.isVisible()
+                                    && w.isDisplayed()
+                                    && w.mActivityRecord != null
+                                    && !addedActivities.contains(
+                                    w.mActivityRecord.mActivityComponent)
+                                    && w.mActivityRecord.isVisible()
+                                    && w.isVisibleNow()) {
+                                addedActivities.add(w.mActivityRecord.mActivityComponent);
+                                result.add(w.mActivityRecord);
+                            }
+                        },
+                        true /* traverseTopToBottom */);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Must be called when a screenshot is taken via hardware chord.
+     *
+     * Notifies all registered visible activities that have registered for screencapture callback,
+     * Returns a list of visible apps component names.
+     */
+    @Override
+    public List<ComponentName> notifyScreenshotListeners(int displayId) {
+        // make sure caller is SysUI.
+        if (!checkCallingPermission(STATUS_BAR_SERVICE,
+                "notifyScreenshotListeners()")) {
+            throw new SecurityException("Requires STATUS_BAR_SERVICE permission");
+        }
+        synchronized (mGlobalLock) {
+            ArraySet<ComponentName> notifiedApps = new ArraySet<>();
+            ArraySet<ActivityRecord> visibleApps = getVisibleActivityRecords(displayId);
+            for (ActivityRecord ar : visibleApps) {
+                if (ar.isRegisteredForScreenCaptureCallback()) {
+                    ar.reportScreenCaptured();
+                    notifiedApps.add(ar.mActivityComponent);
+                }
+            }
+            return List.copyOf(notifiedApps);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 2980c76..7dfc132 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1015,7 +1015,7 @@
 
     /**
      * Returns display UI context list which there is any app window shows or starting activities
-     * int this process.
+     * in this process.
      */
     public void getDisplayContextsWithErrorDialogs(List<Context> displayContexts) {
         if (displayContexts == null) {
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 0963e3b..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);
+                }
             }
         }
 
@@ -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.");
     }
 
@@ -10132,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);
         }
@@ -20104,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 =
@@ -20184,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(
@@ -20196,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());
@@ -20316,4 +20335,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING
index 9fe090a..3976a70 100644
--- a/services/incremental/TEST_MAPPING
+++ b/services/incremental/TEST_MAPPING
@@ -36,5 +36,10 @@
         }
       ]
     }
+  ],
+  "kernel-presubmit": [
+    {
+      "name": "CtsIncrementalInstallHostTestCases"
+    }
   ]
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b1cc306..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);
                 }
             };
 
diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp
index f7efcd1..e711cab 100644
--- a/services/tests/PackageManagerServiceTests/server/Android.bp
+++ b/services/tests/PackageManagerServiceTests/server/Android.bp
@@ -32,7 +32,6 @@
         "services.people",
         "services.usage",
         "guava",
-        "guava-android-testlib",
         "androidx.test.core",
         "androidx.test.ext.truth",
         "androidx.test.runner",
diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
index 33ac735..ea0481e 100644
--- a/services/tests/mockingservicestests/AndroidManifest.xml
+++ b/services/tests/mockingservicestests/AndroidManifest.xml
@@ -43,6 +43,9 @@
     <!-- needed by TrustManagerServiceTest to access LockSettings' secure storage -->
     <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
 
+    <!-- needed by GameManagerServiceTest because GameManager creates a UidObserver -->
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
     <application android:testOnly="true"
                  android:debuggable="true">
         <uses-library android:name="android.test.runner" />
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/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index c4c50424..32243f0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -36,6 +36,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -45,6 +46,7 @@
 
 import android.Manifest;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.GameManager;
 import android.app.GameModeConfiguration;
 import android.app.GameModeInfo;
@@ -2212,4 +2214,106 @@
         assertEquals(expectedInterventionListOutput,
                 gameManagerService.getInterventionList(mPackageName, USER_ID_1));
     }
+
+    @Test
+    public void testGamePowerMode_gamePackage() throws Exception {
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages = {mPackageName};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
+    }
+
+    @Test
+    public void testGamePowerMode_twoGames() throws Exception {
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages1 = {mPackageName};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages1);
+        String someGamePkg = "some.game";
+        String[] packages2 = {someGamePkg};
+        int somePackageId = DEFAULT_PACKAGE_UID + 1;
+        when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
+        HashMap<Integer, Boolean> powerState = new HashMap<>();
+        doAnswer(inv -> powerState.put(inv.getArgument(0), inv.getArgument(1)))
+                .when(mMockPowerManager).setPowerMode(anyInt(), anyBoolean());
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        assertTrue(powerState.get(Mode.GAME));
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        assertTrue(powerState.get(Mode.GAME));
+        gameManagerService.mUidObserver.onUidStateChanged(
+                somePackageId, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+        assertFalse(powerState.get(Mode.GAME));
+    }
+
+    @Test
+    public void testGamePowerMode_twoGamesOverlap() throws Exception {
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages1 = {mPackageName};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages1);
+        String someGamePkg = "some.game";
+        String[] packages2 = {someGamePkg};
+        int somePackageId = DEFAULT_PACKAGE_UID + 1;
+        when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                somePackageId, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
+    }
+
+    @Test
+    public void testGamePowerMode_released() throws Exception {
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages = {mPackageName};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
+    }
+
+    @Test
+    public void testGamePowerMode_noPackage() throws Exception {
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages = {};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, true);
+    }
+
+    @Test
+    public void testGamePowerMode_notAGamePackage() throws Exception {
+        mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE);
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages = {"someapp"};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, true);
+    }
+
+    @Test
+    public void testGamePowerMode_notAGamePackageNotReleased() throws Exception {
+        mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE);
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        String[] packages = {"someapp"};
+        when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+        gameManagerService.mUidObserver.onUidStateChanged(
+                DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+        verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, false);
+    }
 }
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/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index d2ee9ff..6df54a6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -19,6 +19,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
 import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
 import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
@@ -132,6 +133,98 @@
     }
 
     @Test
+    public void testCanRunInBatterySaver_regular() {
+        final JobInfo jobInfo =
+                new JobInfo.Builder(101, new ComponentName("foo", "bar")).build();
+        JobStatus job = createJobStatus(jobInfo);
+        assertFalse(job.canRunInBatterySaver());
+        job.disallowRunInBatterySaverAndDoze();
+        assertFalse(job.canRunInBatterySaver());
+    }
+
+    @Test
+    public void testCanRunInBatterySaver_expedited() {
+        final JobInfo jobInfo =
+                new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+                        .setExpedited(true)
+                        .build();
+        JobStatus job = createJobStatus(jobInfo);
+        markExpeditedQuotaApproved(job, true);
+        assertTrue(job.canRunInBatterySaver());
+        job.disallowRunInBatterySaverAndDoze();
+        assertFalse(job.canRunInBatterySaver());
+
+        // Reset the job
+        job = createJobStatus(jobInfo);
+        markExpeditedQuotaApproved(job, true);
+        spyOn(job);
+        when(job.shouldTreatAsExpeditedJob()).thenReturn(false);
+        job.startedAsExpeditedJob = true;
+        assertTrue(job.canRunInBatterySaver());
+        job.disallowRunInBatterySaverAndDoze();
+        assertFalse(job.canRunInBatterySaver());
+    }
+
+    @Test
+    public void testCanRunInBatterySaver_userInitiated() {
+        final JobInfo jobInfo =
+                new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+                        .setUserInitiated(true)
+                        .build();
+        JobStatus job = createJobStatus(jobInfo);
+        assertTrue(job.canRunInBatterySaver());
+        // User-initiated privilege should trump bs & doze requirement.
+        job.disallowRunInBatterySaverAndDoze();
+        assertTrue(job.canRunInBatterySaver());
+    }
+
+    @Test
+    public void testCanRunInDoze_regular() {
+        final JobInfo jobInfo =
+                new JobInfo.Builder(101, new ComponentName("foo", "bar")).build();
+        JobStatus job = createJobStatus(jobInfo);
+        assertFalse(job.canRunInDoze());
+        job.disallowRunInBatterySaverAndDoze();
+        assertFalse(job.canRunInDoze());
+    }
+
+    @Test
+    public void testCanRunInDoze_expedited() {
+        final JobInfo jobInfo =
+                new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+                        .setExpedited(true)
+                        .build();
+        JobStatus job = createJobStatus(jobInfo);
+        markExpeditedQuotaApproved(job, true);
+        assertTrue(job.canRunInDoze());
+        job.disallowRunInBatterySaverAndDoze();
+        assertFalse(job.canRunInDoze());
+
+        // Reset the job
+        job = createJobStatus(jobInfo);
+        markExpeditedQuotaApproved(job, true);
+        spyOn(job);
+        when(job.shouldTreatAsExpeditedJob()).thenReturn(false);
+        job.startedAsExpeditedJob = true;
+        assertTrue(job.canRunInDoze());
+        job.disallowRunInBatterySaverAndDoze();
+        assertFalse(job.canRunInDoze());
+    }
+
+    @Test
+    public void testCanRunInDoze_userInitiated() {
+        final JobInfo jobInfo =
+                new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+                        .setUserInitiated(true)
+                        .build();
+        JobStatus job = createJobStatus(jobInfo);
+        assertTrue(job.canRunInDoze());
+        // User-initiated privilege should trump bs & doze requirement.
+        job.disallowRunInBatterySaverAndDoze();
+        assertTrue(job.canRunInDoze());
+    }
+
+    @Test
     public void testMediaBackupExemption_lateConstraint() {
         final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
                 .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
@@ -907,6 +1000,13 @@
         assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, false));
     }
 
+    private void markExpeditedQuotaApproved(JobStatus job, boolean isApproved) {
+        if (job.isRequestedExpeditedJob()) {
+            job.setExpeditedJobQuotaApproved(sElapsedRealtimeClock.millis(), isApproved);
+            job.setExpeditedJobTareApproved(sElapsedRealtimeClock.millis(), isApproved);
+        }
+    }
+
     private void markImplicitConstraintsSatisfied(JobStatus job, boolean isSatisfied) {
         job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied);
         job.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied);
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/BinaryTransparencyServiceTest.java b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
index 653ed1a..0b25f38 100644
--- a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
@@ -17,39 +17,85 @@
 package com.android.server;
 
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.job.JobScheduler;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.SensorProperties;
+import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorProperties;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.SystemProperties;
+import android.provider.DeviceConfig;
+import android.util.Log;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.internal.util.FrameworkStatsLog;
+
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.io.FileDescriptor;
 import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
 public class BinaryTransparencyServiceTest {
+    private static final String TAG = "BinaryTransparencyServiceTest";
+
     private Context mContext;
     private BinaryTransparencyService mBinaryTransparencyService;
     private BinaryTransparencyService.BinaryTransparencyServiceImpl mTestInterface;
+    private DeviceConfig.Properties mOriginalBiometricsFlags;
+
+    @Mock
+    private BinaryTransparencyService.BiometricLogger mBiometricLogger;
+    @Mock
+    private FingerprintManager mFpManager;
+    @Mock
+    private FaceManager mFaceManager;
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
         mContext = spy(ApplicationProvider.getApplicationContext());
-        mBinaryTransparencyService = new BinaryTransparencyService(mContext);
+        mBinaryTransparencyService = new BinaryTransparencyService(mContext, mBiometricLogger);
         mTestInterface = mBinaryTransparencyService.new BinaryTransparencyServiceImpl();
+        mOriginalBiometricsFlags = DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BIOMETRICS);
+
+        when(mContext.getSystemService(FingerprintManager.class)).thenReturn(mFpManager);
+        when(mContext.getSystemService(FaceManager.class)).thenReturn(mFaceManager);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        try {
+            DeviceConfig.setProperties(mOriginalBiometricsFlags);
+        } catch (DeviceConfig.BadConfigException e) {
+            Log.e(TAG, "Failed to reset biometrics flags to the original values before test. "
+                    + e);
+        }
     }
 
     private void prepSignedInfo() {
@@ -120,4 +166,105 @@
         }
     }
 
+    @Test
+    public void testCollectBiometricProperties_disablesFeature() {
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BIOMETRICS,
+                BinaryTransparencyService.KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION,
+                Boolean.FALSE.toString(),
+                false /* makeDefault */);
+
+        mBinaryTransparencyService.collectBiometricProperties();
+
+        verify(mFpManager, never()).getSensorPropertiesInternal();
+        verify(mFaceManager, never()).getSensorProperties();
+    }
+
+    @Test
+    public void testCollectBiometricProperties_enablesFeature() {
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BIOMETRICS,
+                BinaryTransparencyService.KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION,
+                Boolean.TRUE.toString(),
+                false /* makeDefault */);
+
+        mBinaryTransparencyService.collectBiometricProperties();
+
+        verify(mFpManager, times(1)).getSensorPropertiesInternal();
+        verify(mFaceManager, times(1)).getSensorProperties();
+    }
+
+    @Test
+    public void testCollectBiometricProperties_enablesFeature_logsFingerprintProperties() {
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BIOMETRICS,
+                BinaryTransparencyService.KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION,
+                Boolean.TRUE.toString(),
+                false /* makeDefault */);
+        final List<FingerprintSensorPropertiesInternal> props = List.of(
+                new FingerprintSensorPropertiesInternal(
+                        1 /* sensorId */,
+                        SensorProperties.STRENGTH_STRONG,
+                        5 /* maxEnrollmentsPerUser */,
+                        List.of(new ComponentInfoInternal("sensor" /* componentId */,
+                                "vendor/model/revision" /* hardwareVersion */,
+                                "1.01" /* firmwareVersion */, "00000001" /* serialNumber */,
+                                "" /* softwareVersion */)),
+                        FingerprintSensorProperties.TYPE_REAR,
+                        true /* resetLockoutRequiresHardwareAuthToken */));
+        when(mFpManager.getSensorPropertiesInternal()).thenReturn(props);
+
+        mBinaryTransparencyService.collectBiometricProperties();
+
+        verify(mBiometricLogger, times(1)).logStats(
+                eq(1) /* sensorId */,
+                eq(FrameworkStatsLog
+                        .BIOMETRIC_PROPERTIES_COLLECTED__MODALITY__MODALITY_FINGERPRINT),
+                eq(FrameworkStatsLog
+                        .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_FP_REAR),
+                eq(FrameworkStatsLog
+                        .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_STRENGTH__STRENGTH_STRONG),
+                eq("sensor") /* componentId */,
+                eq("vendor/model/revision") /* hardwareVersion */,
+                eq("1.01") /* firmwareVersion */,
+                eq("00000001") /* serialNumber */,
+                eq("") /* softwareVersion */
+        );
+    }
+
+    @Test
+    public void testCollectBiometricProperties_enablesFeature_logsFaceProperties() {
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BIOMETRICS,
+                BinaryTransparencyService.KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION,
+                Boolean.TRUE.toString(),
+                false /* makeDefault */);
+        final List<FaceSensorProperties> props = List.of(FaceSensorProperties.from(
+                        new FaceSensorPropertiesInternal(
+                                1 /* sensorId */,
+                                SensorProperties.STRENGTH_CONVENIENCE,
+                                1 /* maxEnrollmentsPerUser */,
+                                List.of(new ComponentInfoInternal("sensor" /* componentId */,
+                                        "vendor/model/revision" /* hardwareVersion */,
+                                        "1.01" /* firmwareVersion */, "00000001" /* serialNumber */,
+                                        "" /* softwareVersion */)),
+                                FaceSensorProperties.TYPE_RGB,
+                                true /* supportsFaceDetection */,
+                                true /* supportsSelfIllumination */,
+                                true /* resetLockoutRequiresHardwareAuthToken */)));
+        when(mFaceManager.getSensorProperties()).thenReturn(props);
+
+        mBinaryTransparencyService.collectBiometricProperties();
+
+        verify(mBiometricLogger, times(1)).logStats(
+                eq(1) /* sensorId */,
+                eq(FrameworkStatsLog
+                        .BIOMETRIC_PROPERTIES_COLLECTED__MODALITY__MODALITY_FACE),
+                eq(FrameworkStatsLog
+                        .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_TYPE__SENSOR_FACE_RGB),
+                eq(FrameworkStatsLog
+                        .BIOMETRIC_PROPERTIES_COLLECTED__SENSOR_STRENGTH__STRENGTH_CONVENIENCE),
+                eq("sensor") /* componentId */,
+                eq("vendor/model/revision") /* hardwareVersion */,
+                eq("1.01") /* firmwareVersion */,
+                eq("00000001") /* serialNumber */,
+                eq("") /* softwareVersion */
+        );
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index aec70f4..6216c66 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -16,16 +16,23 @@
 
 package com.android.server.biometrics;
 
+import static android.Manifest.permission.MANAGE_BIOMETRIC;
+import static android.Manifest.permission.TEST_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_SUCCESS;
 
 import static junit.framework.Assert.assertEquals;
 
+import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -53,11 +60,14 @@
 import com.android.internal.R;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.stubbing.Stubber;
 
 import java.util.List;
 
@@ -65,11 +75,13 @@
 @SmallTest
 public class AuthServiceTest {
 
-    private static final String TAG = "AuthServiceTest";
     private static final String TEST_OP_PACKAGE_NAME = "test_package";
 
     private AuthService mAuthService;
 
+    @Rule
+    public MockitoRule mockitorule = MockitoJUnit.rule();
+
     @Mock
     private Context mContext;
     @Mock
@@ -97,8 +109,6 @@
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
         // Placeholder test config
         final String[] config = {
                 "0:2:15", // ID0:Fingerprint:Strong
@@ -123,10 +133,13 @@
         when(mInjector.getIrisService()).thenReturn(mIrisService);
         when(mInjector.getAppOps(any())).thenReturn(mAppOpsManager);
         when(mInjector.isHidlDisabled(any())).thenReturn(false);
+
+        setInternalAndTestBiometricPermissions(mContext, false /* hasPermission */);
     }
 
     @Test
     public void testRegisterNullService_doesNotRegister() throws Exception {
+        setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
 
         // Config contains Fingerprint, Iris, Face, but services are all null
 
@@ -260,6 +273,40 @@
     }
 
     @Test
+    public void testAuthenticate_throwsWhenUsingTestConfigurations() {
+        final PromptInfo promptInfo = mock(PromptInfo.class);
+        when(promptInfo.containsPrivateApiConfigurations()).thenReturn(false);
+        when(promptInfo.containsTestConfigurations()).thenReturn(true);
+
+        testAuthenticate_throwsWhenUsingTestConfigurations(promptInfo);
+    }
+
+    @Test
+    public void testAuthenticate_throwsWhenUsingPrivateApis() {
+        final PromptInfo promptInfo = mock(PromptInfo.class);
+        when(promptInfo.containsPrivateApiConfigurations()).thenReturn(true);
+        when(promptInfo.containsTestConfigurations()).thenReturn(false);
+
+        testAuthenticate_throwsWhenUsingTestConfigurations(promptInfo);
+    }
+
+    private void testAuthenticate_throwsWhenUsingTestConfigurations(PromptInfo promptInfo) {
+        mAuthService = new AuthService(mContext, mInjector);
+        mAuthService.onStart();
+
+        assertThrows(SecurityException.class, () -> {
+            mAuthService.mImpl.authenticate(
+                    null /* token */,
+                    10 /* sessionId */,
+                    2 /* userId */,
+                    mReceiver,
+                    TEST_OP_PACKAGE_NAME,
+                    promptInfo);
+            waitForIdle();
+        });
+    }
+
+    @Test
     public void testCanAuthenticate_callsBiometricServiceCanAuthenticate() throws Exception {
         mAuthService = new AuthService(mContext, mInjector);
         mAuthService.onStart();
@@ -283,8 +330,10 @@
     }
 
     @Test
-    public void testHasEnrolledBiometrics_callsBiometricServiceHasEnrolledBiometrics() throws
-            Exception {
+    public void testHasEnrolledBiometrics_callsBiometricServiceHasEnrolledBiometrics()
+            throws Exception {
+        setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
+
         mAuthService = new AuthService(mContext, mInjector);
         mAuthService.onStart();
 
@@ -307,6 +356,8 @@
     @Test
     public void testRegisterKeyguardCallback_callsBiometricServiceRegisterKeyguardCallback()
             throws Exception {
+        setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
+
         mAuthService = new AuthService(mContext, mInjector);
         mAuthService.onStart();
 
@@ -320,6 +371,20 @@
                 eq(callback), eq(UserHandle.getCallingUserId()));
     }
 
+    private static void setInternalAndTestBiometricPermissions(
+            Context context, boolean hasPermission) {
+        for (String p : List.of(TEST_BIOMETRIC, MANAGE_BIOMETRIC, USE_BIOMETRIC_INTERNAL)) {
+            when(context.checkCallingPermission(eq(p))).thenReturn(hasPermission
+                    ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
+            when(context.checkCallingOrSelfPermission(eq(p))).thenReturn(hasPermission
+                    ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
+            final Stubber doPermCheck =
+                    hasPermission ? doNothing() : doThrow(SecurityException.class);
+            doPermCheck.when(context).enforceCallingPermission(eq(p), any());
+            doPermCheck.when(context).enforceCallingOrSelfPermission(eq(p), any());
+        }
+    }
+
     private static void waitForIdle() {
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
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
index d71e1ba..c7fb97f 100644
--- 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
@@ -35,6 +35,9 @@
 @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(
@@ -171,10 +174,123 @@
                         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("name");
+        parcelableCallBuilder.setCallerDisplayName(CALLER_DISPLAY_NAME);
+        parcelableCallBuilder.setContactDisplayName(CONTACT_DISPLAY_NAME);
         parcelableCallBuilder.setCapabilities(capabilities);
         parcelableCallBuilder.setState(state);
         parcelableCallBuilder.setConferenceableCallIds(Collections.emptyList());
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/hdmi/BaseAbsoluteVolumeControlTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeControlTest.java
index 93b151e..9f295b8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeControlTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeControlTest.java
@@ -175,7 +175,11 @@
 
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
         hdmiPortInfos[0] =
-                new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+                new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         mNativeWrapper.setPortInfo(hdmiPortInfos);
         mNativeWrapper.setPortConnectionStatus(1, true);
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
index 4d8d25a..9c1b670 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
@@ -137,11 +137,17 @@
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
         hdmiPortInfos[0] =
-                new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_1,
-                                 true, false, false);
+                new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_1)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         hdmiPortInfos[1] =
-                new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_2,
-                                 true, false, false);
+                new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_PLAYBACK_2)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         mNativeWrapper.setPortInfo(hdmiPortInfos);
         mHdmiControlService.initService();
         mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index bb50a89..93508d0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -112,7 +112,11 @@
     public HdmiPortInfo[] nativeGetPortInfos() {
         if (mHdmiPortInfo == null) {
             mHdmiPortInfo = new HdmiPortInfo[1];
-            mHdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
+            mHdmiPortInfo[0] = new HdmiPortInfo.Builder(1, 1, 0x1000)
+                    .setCecSupported(true)
+                    .setMhlSupported(true)
+                    .setArcSupported(true)
+                    .build();
         }
         return mHdmiPortInfo;
     }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index f27587e..e3d9558 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -118,7 +118,11 @@
 
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
         hdmiPortInfos[0] =
-                new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+                new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         mNativeWrapper.setPortInfo(hdmiPortInfos);
         mNativeWrapper.setPortConnectionStatus(1, true);
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 90acc99..f5c0f2a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -197,17 +197,29 @@
         mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnabled(true);
         mHdmiPortInfo = new HdmiPortInfo[4];
         mHdmiPortInfo[0] =
-            new HdmiPortInfo(
-                0, HdmiPortInfo.PORT_INPUT, SELF_PHYSICAL_ADDRESS, true, false, false);
+            new HdmiPortInfo.Builder(0, HdmiPortInfo.PORT_INPUT, SELF_PHYSICAL_ADDRESS)
+                    .setCecSupported(true)
+                    .setMhlSupported(false)
+                    .setArcSupported(false)
+                    .build();
         mHdmiPortInfo[1] =
-            new HdmiPortInfo(
-                2, HdmiPortInfo.PORT_INPUT, HDMI_1_PHYSICAL_ADDRESS, true, false, false);
+            new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, HDMI_1_PHYSICAL_ADDRESS)
+                    .setCecSupported(true)
+                    .setMhlSupported(false)
+                    .setArcSupported(false)
+                    .build();
         mHdmiPortInfo[2] =
-            new HdmiPortInfo(
-                1, HdmiPortInfo.PORT_INPUT, HDMI_2_PHYSICAL_ADDRESS, true, false, false);
+            new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, HDMI_2_PHYSICAL_ADDRESS)
+                    .setCecSupported(true)
+                    .setMhlSupported(false)
+                    .setArcSupported(false)
+                    .build();
         mHdmiPortInfo[3] =
-            new HdmiPortInfo(
-                4, HdmiPortInfo.PORT_INPUT, HDMI_3_PHYSICAL_ADDRESS, true, false, false);
+            new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_INPUT, HDMI_3_PHYSICAL_ADDRESS)
+                    .setCecSupported(true)
+                    .setMhlSupported(false)
+                    .setArcSupported(false)
+                    .build();
         mNativeWrapper.setPortInfo(mHdmiPortInfo);
         mHdmiControlService.initService();
         mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index dfab207..beba9c6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -150,7 +150,11 @@
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
         hdmiPortInfos[0] =
-                new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+                new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         mNativeWrapper.setPortInfo(hdmiPortInfos);
         mNativeWrapper.setPortConnectionStatus(1, true);
         mHdmiControlService.initService();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 3796ce9..9c5c0d4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -189,7 +189,11 @@
         mLocalDevices.add(mHdmiLocalDevice);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
         hdmiPortInfos[0] =
-                new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+                new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         mNativeWrapper.setPortInfo(hdmiPortInfos);
         mNativeWrapper.setPortConnectionStatus(1, true);
         mHdmiControlService.initService();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index cb1e78b..ccfc2b9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -193,10 +193,19 @@
         mHdmiControlService.setEarcController(mHdmiEarcController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
-        hdmiPortInfos[0] =
-                new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false, false);
+        hdmiPortInfos[0] = new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, 0x1000)
+                .setCecSupported(true)
+                .setMhlSupported(false)
+                .setArcSupported(false)
+                .setEarcSupported(false)
+                .build();
         hdmiPortInfos[1] =
-                new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, true, true);
+                new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, 0x2000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(true)
+                        .setEarcSupported(true)
+                        .build();
         mNativeWrapper.setPortInfo(hdmiPortInfos);
         mHdmiControlService.initService();
         mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
index a82a79f..d341153 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -92,15 +92,35 @@
 
         mHdmiPortInfo = new HdmiPortInfo[5];
         mHdmiPortInfo[0] =
-                new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
+                new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, 0x2100)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         mHdmiPortInfo[1] =
-                new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2200, true, false, false);
+                new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, 0x2200)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         mHdmiPortInfo[2] =
-                new HdmiPortInfo(3, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
+                new HdmiPortInfo.Builder(3, HdmiPortInfo.PORT_INPUT, 0x2000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         mHdmiPortInfo[3] =
-                new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false);
+                new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_INPUT, 0x3000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         mHdmiPortInfo[4] =
-                new HdmiPortInfo(5, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+                new HdmiPortInfo.Builder(5, HdmiPortInfo.PORT_OUTPUT, 0x0000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         mNativeWrapper.setPortInfo(mHdmiPortInfo);
         mHdmiCecNetwork.initPortInfo();
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index 8e5bb13..55e8b20 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -99,7 +99,11 @@
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
         hdmiPortInfos[0] =
-                new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+                new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         mNativeWrapper.setPortInfo(hdmiPortInfos);
         mNativeWrapper.setPortConnectionStatus(1, true);
         mHdmiControlService.initService();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index cd6dfbf..8baad61 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -139,13 +139,33 @@
         mLocalDevices.add(mPlaybackDeviceSpy);
         mHdmiPortInfo = new HdmiPortInfo[4];
         mHdmiPortInfo[0] =
-                new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false, false);
+                new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, 0x2100)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .setEarcSupported(false)
+                        .build();
         mHdmiPortInfo[1] =
-                new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2200, true, false, false, false);
+                new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, 0x2200)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .setEarcSupported(false)
+                        .build();
         mHdmiPortInfo[2] =
-                new HdmiPortInfo(3, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, true, true);
+                new HdmiPortInfo.Builder(3, HdmiPortInfo.PORT_INPUT, 0x2000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(true)
+                        .setEarcSupported(true)
+                        .build();
         mHdmiPortInfo[3] =
-                new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false, false);
+                new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_INPUT, 0x3000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .setEarcSupported(false)
+                        .build();
         mNativeWrapper.setPortInfo(mHdmiPortInfo);
         mHdmiControlServiceSpy.initService();
         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index a623841..89743cd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -102,9 +102,17 @@
         mTestLooper.dispatchAll();
         HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[2];
         hdmiPortInfo[0] =
-                new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false);
+                new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, 0x1000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         hdmiPortInfo[1] =
-                new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
+                new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, 0x2000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         mNativeWrapper.setPortInfo(hdmiPortInfo);
         mHdmiControlService.initService();
         mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
index 4e8cf4a..5b1bdf6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
@@ -180,8 +180,11 @@
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
         hdmiPortInfos[0] =
-                new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_AVR,
-                        true, false, false);
+                new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_AVR)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         mNativeWrapper.setPortInfo(hdmiPortInfos);
         mHdmiControlService.initService();
         mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 70f9e5c..c40cd0e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -102,9 +102,17 @@
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
         hdmiPortInfos[0] =
-                new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false);
+                new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_INPUT, 0x1000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(false)
+                        .build();
         hdmiPortInfos[1] =
-                new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, true);
+                new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, 0x2000)
+                        .setCecSupported(true)
+                        .setMhlSupported(false)
+                        .setArcSupported(true)
+                        .build();
         mNativeWrapper.setPortInfo(hdmiPortInfos);
         mHdmiControlService.initService();
         mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
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/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/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 3b7b5eb..9ff8756 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -2603,6 +2603,133 @@
     }
 
     @Test
+    public void testIsHorizontalReachabilityEnabled_splitScreen_false() {
+        mAtm.mDevEnableNonResizableMultiWindow = true;
+        setUpDisplaySizeWithApp(2800, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true);
+        final TestSplitOrganizer organizer =
+                new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+
+        // Unresizable portrait-only activity.
+        prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_PORTRAIT);
+
+        // Move activity to split screen which takes half of the screen.
+        mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+        organizer.mPrimary.setBounds(0, 0, 1400, 1000);
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+        // Horizontal reachability is disabled because the app is in split screen.
+        assertFalse(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+    }
+
+    @Test
+    public void testIsVerticalReachabilityEnabled_splitScreen_false() {
+        mAtm.mDevEnableNonResizableMultiWindow = true;
+        setUpDisplaySizeWithApp(1000, 2800);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
+        final TestSplitOrganizer organizer =
+                new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+
+        // Unresizable landscape-only activity.
+        prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_LANDSCAPE);
+
+        // Move activity to split screen which takes half of the screen.
+        mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+        organizer.mPrimary.setBounds(0, 0, 1000, 1400);
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+        // Vertical reachability is disabled because the app is in split screen.
+        assertFalse(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+    }
+
+    @Test
+    public void testIsVerticalReachabilityEnabled_doesNotMatchParentWidth_false() {
+        setUpDisplaySizeWithApp(1000, 2800);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
+
+        // Unresizable landscape-only activity.
+        prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_LANDSCAPE);
+
+        // Rotate to put activity in size compat mode.
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+        // Activity now in size compat mode.
+        assertTrue(mActivity.inSizeCompatMode());
+
+        // Vertical reachability is disabled because the app does not match parent width
+        assertNotEquals(mActivity.getBounds().width(), mActivity.mDisplayContent.getBounds()
+                .width());
+        assertFalse(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+    }
+
+    @Test
+    public void testIsHorizontalReachabilityEnabled_doesNotMatchParentHeight_false() {
+        setUpDisplaySizeWithApp(2800, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true);
+
+        // Unresizable portrait-only activity.
+        prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_PORTRAIT);
+
+        // Rotate to put activity in size compat mode.
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+        // Activity now in size compat mode.
+        assertTrue(mActivity.inSizeCompatMode());
+
+        // Horizontal reachability is disabled because the app does not match parent height
+        assertNotEquals(mActivity.getBounds().height(), mActivity.mDisplayContent.getBounds()
+                .height());
+        assertFalse(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+    }
+
+    @Test
+    public void testIsHorizontalReachabilityEnabled_inSizeCompatMode_matchesParentHeight_true() {
+        setUpDisplaySizeWithApp(1800, 2200);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true);
+
+        // Unresizable portrait-only activity.
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        // Rotate to put activity in size compat mode.
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+        // Activity now in size compat mode.
+        assertTrue(mActivity.inSizeCompatMode());
+
+        // Horizontal reachability is enabled because the app matches parent height
+        assertEquals(mActivity.getBounds().height(), mActivity.mDisplayContent.getBounds()
+                .height());
+        assertTrue(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+    }
+
+    @Test
+    public void testIsVerticalReachabilityEnabled_inSizeCompatMode_matchesParentWidth_true() {
+        setUpDisplaySizeWithApp(2200, 1800);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
+
+        // Unresizable landscape-only activity.
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+        // Rotate to put activity in size compat mode.
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+        // Activity now in size compat mode.
+        assertTrue(mActivity.inSizeCompatMode());
+
+        // Vertical reachability is enabled because the app matches parent width
+        assertEquals(mActivity.getBounds().width(), mActivity.mDisplayContent.getBounds().width());
+        assertTrue(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+    }
+
+    @Test
     public void testLetterboxDetailsForStatusBar_noLetterbox() {
         setUpDisplaySizeWithApp(2800, 1000);
         addStatusBar(mActivity.mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index d31ae6a..83be4f0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static android.view.Surface.ROTATION_0;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -78,6 +79,12 @@
         final InputMonitor inputMonitor = getInputMonitor();
         spyOn(inputMonitor);
         doNothing().when(inputMonitor).resumeDispatchingLw(any());
+
+        // For devices that set the sysprop ro.bootanim.set_orientation_<display_id>
+        // See DisplayRotation#readDefaultDisplayRotation for context.
+        // Without that, meaning of height and width in context of the tests can be swapped if
+        // the default rotation is 90 or 270.
+        displayRotation.setRotation(ROTATION_0);
     }
 
     public static class Builder {
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/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 28d726e..1a1af3b 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -866,7 +866,8 @@
 
 
     public void simulateDisplayPortAltModeInfo(String portId, int partnerSinkStatus,
-            int cableStatus, int numLanes, IndentingPrintWriter pw) {
+            int cableStatus, int numLanes, boolean hpd, int linkTrainingStatus,
+            IndentingPrintWriter pw) {
         synchronized (mLock) {
             final RawPortInfo portInfo = mSimulatedPorts.get(portId);
             if (portInfo == null) {
@@ -875,7 +876,8 @@
             }
 
             DisplayPortAltModeInfo displayPortAltModeInfo =
-                    new DisplayPortAltModeInfo(partnerSinkStatus, cableStatus, numLanes);
+                    new DisplayPortAltModeInfo(partnerSinkStatus, cableStatus, numLanes, hpd,
+                    linkTrainingStatus);
             portInfo.displayPortAltModeInfo = displayPortAltModeInfo;
             pw.println("Simulating DisplayPort Info: " + displayPortAltModeInfo);
             updatePortsLocked(pw, null);
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 7d84222..0aa1715 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -1169,14 +1169,17 @@
                     mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, "  ")),
                             "", 0);
                 }
-            } else if ("set-displayport-status".equals(args[0]) && args.length == 5) {
+            } else if ("set-displayport-status".equals(args[0]) && args.length == 7) {
                 final String portId = args[1];
                 final int partnerSinkStatus = Integer.parseInt(args[2]);
                 final int cableStatus = Integer.parseInt(args[3]);
                 final int displayPortNumLanes = Integer.parseInt(args[4]);
+                final boolean hpd = Boolean.parseBoolean(args[5]);
+                final int linkTrainingStatus = Integer.parseInt(args[6]);
                 if (mPortManager != null) {
                     mPortManager.simulateDisplayPortAltModeInfo(portId,
-                            partnerSinkStatus, cableStatus, displayPortNumLanes, pw);
+                            partnerSinkStatus, cableStatus, displayPortNumLanes,
+                            hpd, linkTrainingStatus, pw);
                     pw.println();
                     mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, "  ")),
                             "", 0);
@@ -1187,7 +1190,10 @@
                     mPortManager.simulateDisplayPortAltModeInfo(portId,
                             DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN,
                             DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN,
-                            0, pw);
+                            0,
+                            false,
+                            0,
+                            pw);
                     pw.println();
                     mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, "  ")),
                             "", 0);
@@ -1259,7 +1265,13 @@
                 pw.println("Example simulate DisplayPort Alt Mode Changes:");
                 pw.println("  dumpsys usb add-port \"matrix\" dual --displayport");
                 pw.println("  dumpsys usb set-displayport-status \"matrix\" <partner-sink>"
-                        + " <cable> <num-lanes>");
+                        + " <cable> <num-lanes> <hpd> <link-training-status>");
+                pw.println("The required fields are as followed:");
+                pw.println("    <partner-sink>: type DisplayPortAltModeStatus");
+                pw.println("    <cable>: type DisplayPortAltModeStatus");
+                pw.println("    <num-lanes>: type int, expected 0, 2, or 4");
+                pw.println("    <hpd>: type boolean, expected true or false");
+                pw.println("    <link-training-status>: type int with range [0,2]");
                 pw.println("  dumpsys usb reset-displayport-status \"matrix\"");
                 pw.println("reset-displayport-status can also be used in order to set");
                 pw.println("the DisplayPortInfo to default values.");
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
index b9ccace..c7a7a9b 100644
--- a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
@@ -639,7 +639,9 @@
                             altModeData.getDisplayPortAltModeData();
                     return new DisplayPortAltModeInfo(displayPortData.partnerSinkStatus,
                             displayPortData.cableStatus,
-                            toDisplayPortAltModeNumLanesInt(displayPortData.pinAssignment));
+                            toDisplayPortAltModeNumLanesInt(displayPortData.pinAssignment),
+                            displayPortData.hpd,
+                            displayPortData.linkTrainingStatus);
                 }
             }
             return null;
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS b/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
index e5d0370..01b2cb9 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
@@ -1,2 +1,2 @@
-ytai@google.com
+atneya@google.com
 elaurent@google.com
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/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 37d1e94..83b9098 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 "EVDO-0";
+                return "CDMA - EvDo rev. 0";
             case NETWORK_TYPE_EVDO_A:
-                return "EVDO-A";
+                return "CDMA - EvDo rev. A";
             case NETWORK_TYPE_EVDO_B:
-                return "EVDO-B";
+                return "CDMA - EvDo rev. B";
             case NETWORK_TYPE_1xRTT:
-                return "1xRTT";
+                return "CDMA - 1xRTT";
             case NETWORK_TYPE_LTE:
                 return "LTE";
             case NETWORK_TYPE_EHRPD:
-                return "eHRPD";
+                return "CDMA - 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 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 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_RESTRICTIONS:
                 return true;
         }
@@ -18118,44 +18118,4 @@
                 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/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
index 41316d8..8d2af38 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
@@ -17,8 +17,8 @@
 package com.android.server.wm.flicker.launch
 
 import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.RequiresDevice
 import android.view.KeyEvent
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerBuilder
 import com.android.server.wm.flicker.FlickerTest
 import com.android.server.wm.flicker.FlickerTestFactory
@@ -140,6 +140,12 @@
     override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
         super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
+    @Postsubmit
+    @Test
+    override fun navBarWindowIsVisibleAtStartAndEnd() {
+        super.navBarWindowIsVisibleAtStartAndEnd()
+    }
+
     companion object {
         /**
          * Creates the test configurations.
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(