Merge "Use background call for BatteryController#init()" into tm-dev
diff --git a/apct-tests/perftests/core/src/android/libcore/OWNERS b/apct-tests/perftests/core/src/android/libcore/OWNERS
new file mode 100644
index 0000000..2d36574
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 24949
+include platform/libcore:/OWNERS
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 61424ae..1b9cf26 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -27,6 +27,7 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -280,6 +281,18 @@
     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
     public static final long ENABLE_USE_EXACT_ALARM = 218533173L;
 
+    /**
+     * For apps targeting {@link Build.VERSION_CODES#TIRAMISU} or above, the permission
+     * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} will be denied, unless the user explicitly
+     * allows it from Settings.
+     *
+     * TODO (b/226439802): change to EnabledSince(T) after SDK finalization.
+     * @hide
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2)
+    public static final long SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = 226439802L;
+
     @UnsupportedAppUsage
     private final IAlarmManager mService;
     private final Context mContext;
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 f67e8d2..881453f 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -66,6 +66,7 @@
 import android.app.IAlarmManager;
 import android.app.PendingIntent;
 import android.app.compat.CompatChanges;
+import android.app.role.RoleManager;
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
@@ -164,6 +165,7 @@
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
 import java.util.Set;
 import java.util.TimeZone;
@@ -224,6 +226,7 @@
     private ActivityManagerInternal mActivityManagerInternal;
     private final EconomyManagerInternal mEconomyManagerInternal;
     private PackageManagerInternal mPackageManagerInternal;
+    private RoleManager mRoleManager;
     private volatile PermissionManagerServiceInternal mLocalPermissionManager;
 
     final Object mLock = new Object();
@@ -562,6 +565,9 @@
         @VisibleForTesting
         static final String KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED =
                 "kill_on_schedule_exact_alarm_revoked";
+        @VisibleForTesting
+        static final String KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT =
+                "schedule_exact_alarm_denied_by_default";
 
         private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
         private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
@@ -606,6 +612,9 @@
 
         private static final boolean DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = true;
 
+        // TODO(b/226439802): Flip to true.
+        private static final boolean DEFAULT_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = false;
+
         // Minimum futurity of a new alarm
         public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
 
@@ -693,6 +702,14 @@
         public boolean KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED =
                 DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED;
 
+        /**
+         * When this is {@code true}, apps with the change
+         * {@link AlarmManager#SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT} enabled will not get
+         * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} unless the user grants it to them.
+         */
+        public volatile boolean SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT =
+                DEFAULT_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT;
+
         public boolean USE_TARE_POLICY = Settings.Global.DEFAULT_ENABLE_TARE == 1;
 
         private long mLastAllowWhileIdleWhitelistDuration = -1;
@@ -876,6 +893,15 @@
                                     KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED,
                                     DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED);
                             break;
+                        case KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT:
+                            final boolean oldValue = SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT;
+
+                            SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = properties.getBoolean(
+                                    KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT,
+                                    DEFAULT_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT);
+                            handleScheduleExactAlarmDeniedByDefaultChange(oldValue,
+                                    SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT);
+                            break;
                         default:
                             if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
                                 // The quotas need to be updated in order, so we can't just rely
@@ -946,6 +972,15 @@
             }
         }
 
+        private void handleScheduleExactAlarmDeniedByDefaultChange(boolean oldValue,
+                boolean newValue) {
+            if (oldValue == newValue) {
+                return;
+            }
+            mHandler.obtainMessage(AlarmHandler.CHECK_EXACT_ALARM_PERMISSION_ON_FEATURE_TOGGLE,
+                    newValue).sendToTarget();
+        }
+
         private void migrateAlarmsToNewStoreLocked() {
             final AlarmStore newStore = LAZY_BATCHING ? new LazyAlarmStore()
                     : new BatchingAlarmStore();
@@ -1122,6 +1157,9 @@
             pw.print(KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED,
                     KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED);
             pw.println();
+            pw.print(KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT,
+                    SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT);
+            pw.println();
 
             pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY);
             pw.println();
@@ -1892,11 +1930,10 @@
                                 if (hasUseExactAlarmInternal(packageName, uid)) {
                                     return;
                                 }
-
-                                final boolean requested = mExactAlarmCandidates.contains(
-                                        UserHandle.getAppId(uid));
-                                final boolean denyListed =
-                                        mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
+                                if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) {
+                                    // Permission not requested, app op doesn't matter.
+                                    return;
+                                }
 
                                 final int newMode = mAppOps.checkOpNoThrow(
                                         AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid, packageName);
@@ -1913,11 +1950,24 @@
                                         mLastOpScheduleExactAlarm.setValueAt(index, newMode);
                                     }
                                 }
+                                if (oldMode == newMode) {
+                                    return;
+                                }
+                                final boolean allowedByDefault =
+                                        isScheduleExactAlarmAllowedByDefault(packageName, uid);
 
-                                final boolean hadPermission = getScheduleExactAlarmState(requested,
-                                        denyListed, oldMode);
-                                final boolean hasPermission = getScheduleExactAlarmState(requested,
-                                        denyListed, newMode);
+                                final boolean hadPermission;
+                                if (oldMode != AppOpsManager.MODE_DEFAULT) {
+                                    hadPermission = (oldMode == AppOpsManager.MODE_ALLOWED);
+                                } else {
+                                    hadPermission = allowedByDefault;
+                                }
+                                final boolean hasPermission;
+                                if (newMode != AppOpsManager.MODE_DEFAULT) {
+                                    hasPermission = (newMode == AppOpsManager.MODE_ALLOWED);
+                                } else {
+                                    hasPermission = allowedByDefault;
+                                }
 
                                 if (hadPermission && !hasPermission) {
                                     mHandler.obtainMessage(AlarmHandler.REMOVE_EXACT_ALARMS,
@@ -1939,6 +1989,8 @@
                     LocalServices.getService(AppStandbyInternal.class);
             appStandbyInternal.addListener(new AppStandbyTracker());
 
+            mRoleManager = getContext().getSystemService(RoleManager.class);
+
             mMetricsHelper.registerPuller(() -> mAlarmStore);
         }
     }
@@ -2525,19 +2577,6 @@
         }
     }
 
-    private static boolean getScheduleExactAlarmState(boolean requested, boolean denyListed,
-            int appOpMode) {
-        // This does not account for the state of the USE_EXACT_ALARM permission.
-        // The caller should do that separately.
-        if (!requested) {
-            return false;
-        }
-        if (appOpMode == AppOpsManager.MODE_DEFAULT) {
-            return !denyListed;
-        }
-        return appOpMode == AppOpsManager.MODE_ALLOWED;
-    }
-
     boolean hasUseExactAlarmInternal(String packageName, int uid) {
         return isUseExactAlarmEnabled(packageName, UserHandle.getUserId(uid))
                 && (PermissionChecker.checkPermissionForPreflight(getContext(),
@@ -2545,6 +2584,32 @@
                 packageName) == PermissionChecker.PERMISSION_GRANTED);
     }
 
+    /**
+     * Returns whether SCHEDULE_EXACT_ALARM is allowed by default.
+     */
+    boolean isScheduleExactAlarmAllowedByDefault(String packageName, int uid) {
+        if (isScheduleExactAlarmDeniedByDefault(packageName, UserHandle.getUserId(uid))) {
+
+            // This is essentially like changing the protection level of the permission to
+            // (privileged|signature|role|appop), but have to implement this logic to maintain
+            // compatibility for older apps.
+            if (mPackageManagerInternal.isPlatformSigned(packageName)
+                    || mPackageManagerInternal.isUidPrivileged(uid)) {
+                return true;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final List<String> wellbeingHolders = (mRoleManager != null)
+                        ? mRoleManager.getRoleHolders(RoleManager.ROLE_SYSTEM_WELLBEING)
+                        : Collections.emptyList();
+                return wellbeingHolders.contains(packageName);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+        return !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
+    }
+
     boolean hasScheduleExactAlarmInternal(String packageName, int uid) {
         final long start = mStatLogger.getTime();
 
@@ -2560,7 +2625,7 @@
             final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid,
                     packageName);
             if (mode == AppOpsManager.MODE_DEFAULT) {
-                hasPermission = !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
+                hasPermission = isScheduleExactAlarmAllowedByDefault(packageName, uid);
             } else {
                 hasPermission = (mode == AppOpsManager.MODE_ALLOWED);
             }
@@ -2860,6 +2925,13 @@
                 packageName, UserHandle.of(userId));
     }
 
+    private boolean isScheduleExactAlarmDeniedByDefault(String packageName, int userId) {
+        return mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT
+                && CompatChanges.isChangeEnabled(
+                AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, packageName,
+                UserHandle.of(userId));
+    }
+
     @NeverCompile // Avoid size overhead of debugging code.
     void dumpImpl(IndentingPrintWriter pw) {
         synchronized (mLock) {
@@ -3769,26 +3841,27 @@
                 if (!isExactAlarmChangeEnabled(changedPackage, userId)) {
                     continue;
                 }
+                if (isScheduleExactAlarmDeniedByDefault(changedPackage, userId)) {
+                    continue;
+                }
                 if (hasUseExactAlarmInternal(changedPackage, uid)) {
                     continue;
                 }
+                if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) {
+                    // Permission isn't requested, deny list doesn't matter.
+                    continue;
+                }
                 final int appOpMode;
                 synchronized (mLock) {
                     appOpMode = mLastOpScheduleExactAlarm.get(uid,
                             AppOpsManager.opToDefaultMode(AppOpsManager.OP_SCHEDULE_EXACT_ALARM));
                 }
-                final boolean requested = mExactAlarmCandidates.contains(UserHandle.getAppId(uid));
-
-                // added: true => package was added to the deny list
-                // added: false => package was removed from the deny list
-                final boolean hadPermission = getScheduleExactAlarmState(requested, !added,
-                        appOpMode);
-                final boolean hasPermission = getScheduleExactAlarmState(requested, added,
-                        appOpMode);
-
-                if (hadPermission == hasPermission) {
+                if (appOpMode != AppOpsManager.MODE_DEFAULT) {
+                    // Deny list doesn't matter.
                     continue;
                 }
+                // added: true => package was added to the deny list
+                // added: false => package was removed from the deny list
                 if (added) {
                     synchronized (mLock) {
                         removeExactAlarmsOnPermissionRevokedLocked(uid,
@@ -4634,6 +4707,7 @@
         public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11;
         public static final int TARE_AFFORDABILITY_CHANGED = 12;
         public static final int CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE = 13;
+        public static final int CHECK_EXACT_ALARM_PERMISSION_ON_FEATURE_TOGGLE = 14;
 
         AlarmHandler() {
             super(Looper.myLooper());
@@ -4759,6 +4833,35 @@
                         }
                     }
                     break;
+                case CHECK_EXACT_ALARM_PERMISSION_ON_FEATURE_TOGGLE:
+                    final boolean defaultDenied = (Boolean) msg.obj;
+
+                    final int[] startedUserIds = mActivityManagerInternal.getStartedUserIds();
+                    for (int appId : mExactAlarmCandidates) {
+                        for (int userId : startedUserIds) {
+                            uid = UserHandle.getUid(userId, appId);
+
+                            final AndroidPackage packageForUid =
+                                    mPackageManagerInternal.getPackage(uid);
+                            if (packageForUid == null) {
+                                continue;
+                            }
+                            final String pkg = packageForUid.getPackageName();
+                            if (defaultDenied) {
+                                if (!hasScheduleExactAlarmInternal(pkg, uid)
+                                        && !hasUseExactAlarmInternal(pkg, uid)) {
+                                    synchronized (mLock) {
+                                        removeExactAlarmsOnPermissionRevokedLocked(uid, pkg,
+                                                true);
+                                    }
+                                }
+                            } else if (hasScheduleExactAlarmInternal(pkg, uid)) {
+                                sendScheduleExactAlarmPermissionStateChangedBroadcast(pkg,
+                                        UserHandle.getUserId(uid));
+                            }
+                        }
+                    }
+                    break;
                 default:
                     // nope, just ignore it
                     break;
diff --git a/core/java/android/hardware/input/InputDeviceSensorManager.java b/core/java/android/hardware/input/InputDeviceSensorManager.java
index 89db857..8a40d00 100644
--- a/core/java/android/hardware/input/InputDeviceSensorManager.java
+++ b/core/java/android/hardware/input/InputDeviceSensorManager.java
@@ -98,7 +98,7 @@
      */
     private void updateInputDeviceSensorInfoLocked(int deviceId) {
         final InputDevice inputDevice = InputDevice.getDevice(deviceId);
-        if (inputDevice.hasSensor()) {
+        if (inputDevice != null && inputDevice.hasSensor()) {
             final InputSensorInfo[] sensorInfos =
                     mInputManager.getSensorList(deviceId);
             populateSensorsForInputDeviceLocked(deviceId, sensorInfos);
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 03d1151..dc38db2 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -151,6 +151,8 @@
 
         private boolean mDrawLegacyNavigationBarBackground;
 
+        private final Rect mTempRect = new Rect();
+
         Impl(@NonNull InputMethodService inputMethodService) {
             mService = inputMethodService;
         }
@@ -257,23 +259,22 @@
                 switch (originalInsets.touchableInsets) {
                     case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
                         if (inputFrame.getVisibility() == View.VISIBLE) {
-                            touchableRegion = new Region(inputFrame.getLeft(),
-                                    inputFrame.getTop(), inputFrame.getRight(),
-                                    inputFrame.getBottom());
+                            inputFrame.getBoundsOnScreen(mTempRect);
+                            touchableRegion = new Region(mTempRect);
                         }
                         break;
                     case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT:
                         if (inputFrame.getVisibility() == View.VISIBLE) {
-                            touchableRegion = new Region(inputFrame.getLeft(),
-                                    originalInsets.contentTopInsets, inputFrame.getRight(),
-                                    inputFrame.getBottom());
+                            inputFrame.getBoundsOnScreen(mTempRect);
+                            mTempRect.top = originalInsets.contentTopInsets;
+                            touchableRegion = new Region(mTempRect);
                         }
                         break;
                     case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE:
                         if (inputFrame.getVisibility() == View.VISIBLE) {
-                            touchableRegion = new Region(inputFrame.getLeft(),
-                                    originalInsets.visibleTopInsets, inputFrame.getRight(),
-                                    inputFrame.getBottom());
+                            inputFrame.getBoundsOnScreen(mTempRect);
+                            mTempRect.top = originalInsets.visibleTopInsets;
+                            touchableRegion = new Region(mTempRect);
                         }
                         break;
                     case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION:
@@ -281,13 +282,13 @@
                         touchableRegion.set(originalInsets.touchableRegion);
                         break;
                 }
-                final Rect navBarRect = new Rect(decor.getLeft(),
-                        decor.getBottom() - systemInsets.bottom,
+                // Hereafter "mTempRect" means a navigation bar rect.
+                mTempRect.set(decor.getLeft(), decor.getBottom() - systemInsets.bottom,
                         decor.getRight(), decor.getBottom());
                 if (touchableRegion == null) {
-                    touchableRegion = new Region(navBarRect);
+                    touchableRegion = new Region(mTempRect);
                 } else {
-                    touchableRegion.union(navBarRect);
+                    touchableRegion.union(mTempRect);
                 }
 
                 dest.touchableRegion.set(touchableRegion);
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 3abe83b..1b503b1 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -25,12 +25,6 @@
 import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512;
 import static android.net.IpSecAlgorithm.CRYPT_AES_CBC;
 import static android.net.IpSecAlgorithm.CRYPT_AES_CTR;
-import static android.net.eap.EapSessionConfig.EapMsChapV2Config;
-import static android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig;
-import static android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignLocalConfig;
-import static android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignRemoteConfig;
-import static android.net.ipsec.ike.IkeSessionParams.IkeAuthEapConfig;
-import static android.net.ipsec.ike.IkeSessionParams.IkeAuthPskConfig;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 import static com.android.internal.util.Preconditions.checkStringNotEmpty;
@@ -40,6 +34,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.content.pm.PackageManager;
+import android.net.ipsec.ike.IkeDerAsn1DnIdentification;
 import android.net.ipsec.ike.IkeFqdnIdentification;
 import android.net.ipsec.ike.IkeIdentification;
 import android.net.ipsec.ike.IkeIpv4AddrIdentification;
@@ -119,8 +114,8 @@
         DEFAULT_ALGORITHMS = Collections.unmodifiableList(algorithms);
     }
 
-    @NonNull private final String mServerAddr;
-    @NonNull private final String mUserIdentity;
+    @Nullable private final String mServerAddr;
+    @Nullable private final String mUserIdentity;
 
     // PSK authentication
     @Nullable private final byte[] mPresharedKey;
@@ -146,8 +141,8 @@
 
     private Ikev2VpnProfile(
             int type,
-            @NonNull String serverAddr,
-            @NonNull String userIdentity,
+            @Nullable String serverAddr,
+            @Nullable String userIdentity,
             @Nullable byte[] presharedKey,
             @Nullable X509Certificate serverRootCaCert,
             @Nullable String username,
@@ -165,8 +160,6 @@
             @Nullable IkeTunnelConnectionParams ikeTunConnParams) {
         super(type, excludeLocalRoutes, requiresInternetValidation);
 
-        checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
-        checkNotNull(userIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
         checkNotNull(allowedAlgorithms, MISSING_PARAM_MSG_TMPL, "Allowed Algorithms");
 
         mServerAddr = serverAddr;
@@ -191,18 +184,12 @@
         mIsMetered = isMetered;
         mMaxMtu = maxMtu;
         mIsRestrictedToTestNetworks = restrictToTestNetworks;
-
         mIkeTunConnParams = ikeTunConnParams;
 
         validate();
     }
 
     private void validate() {
-        // Server Address not validated except to check an address was provided. This allows for
-        // dual-stack servers and hostname based addresses.
-        checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address");
-        checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
-
         // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
         // networks, the VPN must provide a link fulfilling the stricter of the two conditions
         // (at least that of the IPv6 MTU).
@@ -210,6 +197,15 @@
             throw new IllegalArgumentException("Max MTU must be at least" + IPV6_MIN_MTU);
         }
 
+        // Skip validating the other fields if mIkeTunConnParams is set because the required
+        // information should all come from the mIkeTunConnParams.
+        if (mIkeTunConnParams != null) return;
+
+        // Server Address not validated except to check an address was provided. This allows for
+        // dual-stack servers and hostname based addresses.
+        checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address");
+        checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
+
         switch (mType) {
             case TYPE_IKEV2_IPSEC_USER_PASS:
                 checkNotNull(mUsername, MISSING_PARAM_MSG_TMPL, "Username");
@@ -286,22 +282,31 @@
     /** Retrieves the server address string. */
     @NonNull
     public String getServerAddr() {
-        return mServerAddr;
+        if (mIkeTunConnParams == null) return mServerAddr;
+
+        final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams();
+        return ikeSessionParams.getServerHostname();
     }
 
     /** Retrieves the user identity. */
     @NonNull
     public String getUserIdentity() {
-        return mUserIdentity;
+        if (mIkeTunConnParams == null) return mUserIdentity;
+
+        final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams();
+        return getUserIdentityFromIkeSession(ikeSessionParams);
     }
 
     /**
      * Retrieves the pre-shared key.
      *
-     * <p>May be null if the profile is not using Pre-shared key authentication.
+     * <p>May be null if the profile is not using Pre-shared key authentication, or the profile is
+     * built from an {@link IkeTunnelConnectionParams}.
      */
     @Nullable
     public byte[] getPresharedKey() {
+        if (mIkeTunConnParams != null) return null;
+
         return mPresharedKey == null ? null : Arrays.copyOf(mPresharedKey, mPresharedKey.length);
     }
 
@@ -309,46 +314,62 @@
      * Retrieves the certificate for the server's root CA.
      *
      * <p>May be null if the profile is not using RSA Digital Signature Authentication or
-     * Username/Password authentication
+     * Username/Password authentication, or the profile is built from an
+     * {@link IkeTunnelConnectionParams}.
      */
     @Nullable
     public X509Certificate getServerRootCaCert() {
+        if (mIkeTunConnParams != null) return null;
+
         return mServerRootCaCert;
     }
-
     /**
      * Retrieves the username.
      *
-     * <p>May be null if the profile is not using Username/Password authentication
+     * <p>May be null if the profile is not using Username/Password authentication, or the profile
+     * is built from an {@link IkeTunnelConnectionParams}.
      */
     @Nullable
     public String getUsername() {
+        if (mIkeTunConnParams != null) return null;
+
         return mUsername;
     }
 
     /**
      * Retrieves the password.
      *
-     * <p>May be null if the profile is not using Username/Password authentication
+     * <p>May be null if the profile is not using Username/Password authentication, or the profile
+     * is built from an {@link IkeTunnelConnectionParams}.
      */
     @Nullable
     public String getPassword() {
+        if (mIkeTunConnParams != null) return null;
+
         return mPassword;
     }
 
     /**
      * Retrieves the RSA private key.
      *
-     * <p>May be null if the profile is not using RSA Digital Signature authentication
+     * <p>May be null if the profile is not using RSA Digital Signature authentication, or the
+     * profile is built from an {@link IkeTunnelConnectionParams}.
      */
     @Nullable
     public PrivateKey getRsaPrivateKey() {
+        if (mIkeTunConnParams != null) return null;
+
         return mRsaPrivateKey;
     }
 
-    /** Retrieves the user certificate, if any was set. */
+    /** Retrieves the user certificate, if any was set.
+     *
+     * <p>May be null if the profile is built from an {@link IkeTunnelConnectionParams}.
+     */
     @Nullable
     public X509Certificate getUserCert() {
+        if (mIkeTunConnParams != null) return null;
+
         return mUserCert;
     }
 
@@ -358,9 +379,14 @@
         return mProxyInfo;
     }
 
-    /** Returns all the algorithms allowed by this VPN profile. */
+    /** Returns all the algorithms allowed by this VPN profile.
+     *
+     *  <p>May be an empty list if the profile is built from an {@link IkeTunnelConnectionParams}.
+     */
     @NonNull
     public List<String> getAllowedAlgorithms() {
+        if (mIkeTunConnParams != null) return new ArrayList<>();
+
         return mAllowedAlgorithms;
     }
 
@@ -455,18 +481,25 @@
     @NonNull
     public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException {
         final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */,
-                mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation);
-        profile.type = mType;
-        profile.server = mServerAddr;
-        profile.ipsecIdentifier = mUserIdentity;
+                mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation,
+                mIkeTunConnParams);
+
+        profile.server = getServerAddr();
+        profile.ipsecIdentifier = getUserIdentity();
         profile.proxy = mProxyInfo;
-        profile.setAllowedAlgorithms(mAllowedAlgorithms);
         profile.isBypassable = mIsBypassable;
         profile.isMetered = mIsMetered;
         profile.maxMtu = mMaxMtu;
         profile.areAuthParamsInline = true;
         profile.saveLogin = true;
+        // The other fields should come from mIkeTunConnParams if it's available.
+        if (mIkeTunConnParams != null) {
+            profile.type = VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS;
+            return profile;
+        }
 
+        profile.type = mType;
+        profile.setAllowedAlgorithms(mAllowedAlgorithms);
         switch (mType) {
             case TYPE_IKEV2_IPSEC_USER_PASS:
                 profile.username = mUsername;
@@ -516,10 +549,47 @@
     @NonNull
     public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
             throws GeneralSecurityException {
-        // TODO: Build the VpnProfile from mIkeTunConnParams if it exists.
-        final Builder builder = new Builder(profile.server, profile.ipsecIdentifier);
+        final Builder builder;
+        if (profile.ikeTunConnParams == null) {
+            builder = new Builder(profile.server, profile.ipsecIdentifier);
+            builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
+
+            switch (profile.type) {
+                case TYPE_IKEV2_IPSEC_USER_PASS:
+                    builder.setAuthUsernamePassword(
+                            profile.username,
+                            profile.password,
+                            certificateFromPemString(profile.ipsecCaCert));
+                    break;
+                case TYPE_IKEV2_IPSEC_PSK:
+                    builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret));
+                    break;
+                case TYPE_IKEV2_IPSEC_RSA:
+                    final PrivateKey key;
+                    if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) {
+                        final String alias =
+                                profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length());
+                        key = getPrivateKeyFromAndroidKeystore(alias);
+                    } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) {
+                        key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length()));
+                    } else {
+                        throw new IllegalArgumentException("Invalid RSA private key prefix");
+                    }
+
+                    final X509Certificate userCert =
+                            certificateFromPemString(profile.ipsecUserCert);
+                    final X509Certificate serverRootCa =
+                            certificateFromPemString(profile.ipsecCaCert);
+                    builder.setAuthDigitalSignature(userCert, key, serverRootCa);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Invalid auth method set");
+            }
+        } else {
+            builder = new Builder(profile.ikeTunConnParams);
+        }
+
         builder.setProxy(profile.proxy);
-        builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
         builder.setBypassable(profile.isBypassable);
         builder.setMetered(profile.isMetered);
         builder.setMaxMtu(profile.maxMtu);
@@ -527,36 +597,6 @@
             builder.restrictToTestNetworks();
         }
 
-        switch (profile.type) {
-            case TYPE_IKEV2_IPSEC_USER_PASS:
-                builder.setAuthUsernamePassword(
-                        profile.username,
-                        profile.password,
-                        certificateFromPemString(profile.ipsecCaCert));
-                break;
-            case TYPE_IKEV2_IPSEC_PSK:
-                builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret));
-                break;
-            case TYPE_IKEV2_IPSEC_RSA:
-                final PrivateKey key;
-                if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) {
-                    final String alias =
-                            profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length());
-                    key = getPrivateKeyFromAndroidKeystore(alias);
-                } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) {
-                    key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length()));
-                } else {
-                    throw new IllegalArgumentException("Invalid RSA private key prefix");
-                }
-
-                final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert);
-                final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert);
-                builder.setAuthDigitalSignature(userCert, key, serverRootCa);
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid auth method set");
-        }
-
         if (profile.excludeLocalRoutes && !profile.isBypassable) {
             Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN");
         }
@@ -678,82 +718,13 @@
     }
 
     private static void checkBuilderSetter(boolean constructedFromIkeTunConParams,
-            @NonNull String message) {
+            @NonNull String field) {
         if (constructedFromIkeTunConParams) {
-            throw new IllegalArgumentException("Constructed using IkeTunnelConnectionParams "
-                    + "should not set " + message);
+            throw new IllegalArgumentException(
+                    field + " can't be set with IkeTunnelConnectionParams builder");
         }
     }
 
-    private static int getTypeFromIkeSession(@NonNull IkeSessionParams params) {
-        final IkeAuthConfig config = params.getLocalAuthConfig();
-        if (config instanceof IkeAuthDigitalSignLocalConfig) {
-            return TYPE_IKEV2_IPSEC_RSA;
-        } else if (config instanceof IkeAuthEapConfig) {
-            return TYPE_IKEV2_IPSEC_USER_PASS;
-        } else if (config instanceof IkeAuthPskConfig) {
-            return TYPE_IKEV2_IPSEC_PSK;
-        } else {
-            throw new IllegalStateException("Invalid local IkeAuthConfig");
-        }
-    }
-
-    @Nullable
-    private static String getPasswordFromIkeSession(@NonNull IkeSessionParams params) {
-        if (!(params.getLocalAuthConfig() instanceof IkeAuthEapConfig)) return null;
-
-        final IkeAuthEapConfig ikeAuthEapConfig = (IkeAuthEapConfig) params.getLocalAuthConfig();
-        final EapMsChapV2Config eapMsChapV2Config =
-                ikeAuthEapConfig.getEapConfig().getEapMsChapV2Config();
-        return (eapMsChapV2Config != null) ? eapMsChapV2Config.getPassword() : null;
-    }
-
-    @Nullable
-    private static String getUsernameFromIkeSession(@NonNull IkeSessionParams params) {
-        if (!(params.getLocalAuthConfig() instanceof IkeAuthEapConfig)) return null;
-
-        final IkeAuthEapConfig ikeAuthEapConfig = (IkeAuthEapConfig) params.getLocalAuthConfig();
-        final EapMsChapV2Config eapMsChapV2Config =
-                ikeAuthEapConfig.getEapConfig().getEapMsChapV2Config();
-        return (eapMsChapV2Config != null) ? eapMsChapV2Config.getUsername() : null;
-    }
-
-    @Nullable
-    private static X509Certificate getUserCertFromIkeSession(@NonNull IkeSessionParams params) {
-        if (!(params.getLocalAuthConfig() instanceof IkeAuthDigitalSignLocalConfig)) return null;
-
-        final IkeAuthDigitalSignLocalConfig config =
-                (IkeAuthDigitalSignLocalConfig) params.getLocalAuthConfig();
-        return config.getClientEndCertificate();
-    }
-
-    @Nullable
-    private static X509Certificate getServerRootCaCertFromIkeSession(
-            @NonNull IkeSessionParams params) {
-        if (!(params.getRemoteAuthConfig() instanceof IkeAuthDigitalSignRemoteConfig)) return null;
-
-        final IkeAuthDigitalSignRemoteConfig config =
-                (IkeAuthDigitalSignRemoteConfig) params.getRemoteAuthConfig();
-        return config.getRemoteCaCert();
-    }
-
-    @Nullable
-    private static PrivateKey getRsaPrivateKeyFromIkeSession(@NonNull IkeSessionParams params) {
-        if (!(params.getLocalAuthConfig() instanceof IkeAuthDigitalSignLocalConfig)) return null;
-
-        final IkeAuthDigitalSignLocalConfig config =
-                (IkeAuthDigitalSignLocalConfig) params.getLocalAuthConfig();
-        return config.getPrivateKey();
-    }
-
-    @Nullable
-    private static byte[] getPresharedKeyFromIkeSession(@NonNull IkeSessionParams params) {
-        if (!(params.getLocalAuthConfig() instanceof IkeAuthPskConfig)) return null;
-
-        final IkeAuthPskConfig config = (IkeAuthPskConfig) params.getLocalAuthConfig();
-        return config.getPsk();
-    }
-
     @NonNull
     private static String getUserIdentityFromIkeSession(@NonNull IkeSessionParams params) {
         final IkeIdentification ident = params.getLocalIdentification();
@@ -768,6 +739,8 @@
             return ((IkeIpv4AddrIdentification) ident).ipv4Address.getHostAddress();
         } else if (ident instanceof IkeIpv6AddrIdentification) {
             return ((IkeIpv6AddrIdentification) ident).ipv6Address.getHostAddress();
+        } else if (ident instanceof IkeDerAsn1DnIdentification) {
+            throw new IllegalArgumentException("Unspported ASN.1 encoded identities");
         } else {
             throw new IllegalArgumentException("Unknown IkeIdentification to get user identity");
         }
@@ -776,8 +749,8 @@
     /** A incremental builder for IKEv2 VPN profiles */
     public static final class Builder {
         private int mType = -1;
-        @NonNull private final String mServerAddr;
-        @NonNull private final String mUserIdentity;
+        @Nullable private final String mServerAddr;
+        @Nullable private final String mUserIdentity;
 
         // PSK authentication
         @Nullable private byte[] mPresharedKey;
@@ -831,19 +804,8 @@
             checkNotNull(ikeTunConnParams, MISSING_PARAM_MSG_TMPL, "ikeTunConnParams");
 
             mIkeTunConnParams = ikeTunConnParams;
-
-            final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams();
-            mServerAddr = ikeSessionParams.getServerHostname();
-
-            mType = getTypeFromIkeSession(ikeSessionParams);
-            mUserCert = getUserCertFromIkeSession(ikeSessionParams);
-            mServerRootCaCert = getServerRootCaCertFromIkeSession(ikeSessionParams);
-            mRsaPrivateKey = getRsaPrivateKeyFromIkeSession(ikeSessionParams);
-            mServerRootCaCert = getServerRootCaCertFromIkeSession(ikeSessionParams);
-            mUsername = getUsernameFromIkeSession(ikeSessionParams);
-            mPassword = getPasswordFromIkeSession(ikeSessionParams);
-            mPresharedKey = getPresharedKeyFromIkeSession(ikeSessionParams);
-            mUserIdentity = getUserIdentityFromIkeSession(ikeSessionParams);
+            mServerAddr = null;
+            mUserIdentity = null;
         }
 
         private void resetAuthParams() {
@@ -862,6 +824,10 @@
          * authentication method may be set. This method will overwrite any previously set
          * authentication method.
          *
+         * <p>It's not allowed to set this if this {@link Builder} is constructed from an
+         * {@link IkeTunnelConnectionParams}. This information should be retrieved from
+         * {@link IkeTunnelConnectionParams}
+         *
          * @param user the username to be used for EAP-MSCHAPv2 authentication
          * @param pass the password to be used for EAP-MSCHAPv2 authentication
          * @param serverRootCa the root certificate to be used for verifying the identity of the
@@ -898,6 +864,10 @@
          * Only one authentication method may be set. This method will overwrite any previously set
          * authentication method.
          *
+         * <p>It's not allowed to set this if this {@link Builder} is constructed from an
+         * {@link IkeTunnelConnectionParams}. This information should be retrieved from
+         * {@link IkeTunnelConnectionParams}
+         *
          * @param userCert the username to be used for RSA Digital signiture authentication
          * @param key the PrivateKey instance associated with the user ceritificate, used for
          *     constructing the signature
@@ -936,6 +906,10 @@
          * authentication method may be set. This method will overwrite any previously set
          * authentication method.
          *
+         * <p>It's not allowed to set this if this {@link Builder} is constructed from an
+         * {@link IkeTunnelConnectionParams}. This information should be retrieved from
+         * {@link IkeTunnelConnectionParams}
+         *
          * @param psk the key to be used for Pre-Shared Key authentication
          * @return this {@link Builder} object to facilitate chaining of method calls
          */
@@ -1068,6 +1042,10 @@
          * Authentication, and one that provides Encryption. Authenticated Encryption with
          * Associated Data (AEAD) algorithms provide both Authentication and Encryption.
          *
+         * <p>It's not allowed to set this if this {@link Builder} is constructed from an
+         * {@link IkeTunnelConnectionParams}. This information should be retrieved from
+         * {@link IkeTunnelConnectionParams}
+         *
          * <p>By default, this profile will use any algorithm defined in {@link IpSecAlgorithm},
          * with the exception of those considered insecure (as described above).
          *
@@ -1079,6 +1057,7 @@
         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
         public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) {
             checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames");
+            checkBuilderSetter(mIkeTunConnParams != null, "algorithmNames");
             validateAllowedAlgorithms(algorithmNames);
 
             mAllowedAlgorithms = algorithmNames;
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index 2dac81c..e98d046 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -33,6 +33,7 @@
 import static android.view.RemoteAnimationTargetProto.WINDOW_CONFIGURATION;
 import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
 
+import android.annotation.ColorInt;
 import android.annotation.IntDef;
 import android.app.ActivityManager;
 import android.app.TaskInfo;
@@ -221,6 +222,12 @@
      */
     public boolean hasAnimatingParent;
 
+    /**
+     * The background color of animation in case the task info is not available if the transition
+     * is activity level.
+     */
+    public @ColorInt int backgroundColor;
+
     public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
             Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
             Rect localBounds, Rect screenSpaceBounds,
@@ -279,6 +286,7 @@
         allowEnterPip = in.readBoolean();
         windowType = in.readInt();
         hasAnimatingParent = in.readBoolean();
+        backgroundColor = in.readInt();
     }
 
     @Override
@@ -307,6 +315,7 @@
         dest.writeBoolean(allowEnterPip);
         dest.writeInt(windowType);
         dest.writeBoolean(hasAnimatingParent);
+        dest.writeInt(backgroundColor);
     }
 
     public void dump(PrintWriter pw, String prefix) {
@@ -327,6 +336,7 @@
         pw.print(prefix); pw.print("allowEnterPip="); pw.println(allowEnterPip);
         pw.print(prefix); pw.print("windowType="); pw.print(windowType);
         pw.print(prefix); pw.print("hasAnimatingParent="); pw.print(hasAnimatingParent);
+        pw.print(prefix); pw.print("backgroundColor="); pw.print(backgroundColor);
     }
 
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1328e66..c45a4c7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2767,17 +2767,16 @@
             dispatchApplyInsets(host);
         }
 
+        if (mFirst) {
+            // make sure touch mode code executes by setting cached value
+            // to opposite of the added touch mode.
+            mAttachInfo.mInTouchMode = !mAddedTouchMode;
+            ensureTouchModeLocally(mAddedTouchMode);
+        }
+
         boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
         if (layoutRequested) {
-
-            final Resources res = mView.getContext().getResources();
-
-            if (mFirst) {
-                // make sure touch mode code executes by setting cached value
-                // to opposite of the added touch mode.
-                mAttachInfo.mInTouchMode = !mAddedTouchMode;
-                ensureTouchModeLocally(mAddedTouchMode);
-            } else {
+            if (!mFirst) {
                 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                     windowSizeMayChange = true;
@@ -2797,7 +2796,7 @@
             }
 
             // Ask host how big it wants to be
-            windowSizeMayChange |= measureHierarchy(host, lp, res,
+            windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),
                     desiredWindowWidth, desiredWindowHeight);
         }
 
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 29a9926..aae930e 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -216,10 +216,14 @@
     public String[] getViewRootNames() {
         synchronized (mLock) {
             final int numRoots = mRoots.size();
-            String[] mViewRoots = new String[numRoots];
+            final int windowlessRoots = mWindowlessRoots.size();
+            String[] mViewRoots = new String[numRoots + windowlessRoots];
             for (int i = 0; i < numRoots; ++i) {
                 mViewRoots[i] = getWindowName(mRoots.get(i));
             }
+            for (int i = 0; i < windowlessRoots; ++i) {
+                mViewRoots[i + numRoots] = getWindowName(mWindowlessRoots.get(i));
+            }
             return mViewRoots;
         }
     }
@@ -288,6 +292,10 @@
                 final ViewRootImpl root = mRoots.get(i);
                 if (name.equals(getWindowName(root))) return root.getView();
             }
+            for (int i = mWindowlessRoots.size() - 1; i >= 0; --i) {
+                final ViewRootImpl root = mWindowlessRoots.get(i);
+                if (name.equals(getWindowName(root))) return root.getView();
+            }
         }
 
         return null;
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 5007df5..4b9a957 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -41,14 +41,13 @@
  * @hide
  */
 public class WindowContextController {
-    // TODO(220049234): Disable attach debug logging before shipping.
-    private static final boolean DEBUG_ATTACH = true;
+    private static final boolean DEBUG_ATTACH = false;
     private static final String TAG = "WindowContextController";
 
     /**
-     * {@link AttachStatus.STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a
+     * {@link AttachStatus#STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a
      * {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a
-     * WindowToken after this flag sets to {@link AttachStatus.STATUS_ATTACHED}.
+     * WindowToken after this flag sets to {@link AttachStatus#STATUS_ATTACHED}.
      */
     @VisibleForTesting
     public int mAttachedToDisplayArea = AttachStatus.STATUS_INITIALIZED;
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index bea2c78..cad8b9b 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -51,9 +51,10 @@
     private IWindowSession mWindowSession;
     private IWindow mWindow;
     private static final String TAG = "WindowOnBackDispatcher";
-    private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
-    private static final boolean IS_BACK_PREDICTABILITY_ENABLED = SystemProperties
-            .getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
+    private static final boolean ENABLE_PREDICTIVE_BACK = SystemProperties
+            .getInt("persist.wm.debug.predictive_back", 1) != 0;
+    private static final boolean ALWAYS_ENFORCE_PREDICTIVE_BACK = SystemProperties
+            .getInt("persist.wm.debug.predictive_back_always_enforce", 0) != 0;
 
     /** Convenience hashmap to quickly decide if a callback has been added. */
     private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
@@ -254,18 +255,18 @@
     public static boolean isOnBackInvokedCallbackEnabled(@Nullable Context context) {
         // new back is enabled if the feature flag is enabled AND the app does not explicitly
         // request legacy back.
-        boolean featureFlagEnabled = IS_BACK_PREDICTABILITY_ENABLED;
+        boolean featureFlagEnabled = ENABLE_PREDICTIVE_BACK;
         // If the context is null, we assume true and fallback on the two other conditions.
         boolean appRequestsPredictiveBack =
                 context != null && context.getApplicationInfo().isOnBackInvokedCallbackEnabled();
 
         if (DEBUG) {
             Log.d(TAG, TextUtils.formatSimple("App: %s featureFlagEnabled=%s "
-                            + "appRequestsPredictiveBack=%s",
+                            + "appRequestsPredictiveBack=%s alwaysEnforce=%s",
                     context != null ? context.getApplicationInfo().packageName : "null context",
-                    featureFlagEnabled, appRequestsPredictiveBack));
+                    featureFlagEnabled, appRequestsPredictiveBack, ALWAYS_ENFORCE_PREDICTIVE_BACK));
         }
 
-        return featureFlagEnabled && appRequestsPredictiveBack;
+        return featureFlagEnabled && (appRequestsPredictiveBack || ALWAYS_ENFORCE_PREDICTIVE_BACK);
     }
 }
diff --git a/core/java/com/android/internal/app/AppLocaleStore.java b/core/java/com/android/internal/app/AppLocaleStore.java
index 76e5898..f958385 100644
--- a/core/java/com/android/internal/app/AppLocaleStore.java
+++ b/core/java/com/android/internal/app/AppLocaleStore.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.app;
 
+import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus;
+
 import android.app.LocaleConfig;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -25,41 +27,43 @@
 import java.util.ArrayList;
 import java.util.Locale;
 
-public class AppLocaleStore {
+class AppLocaleStore {
     private static final String TAG = AppLocaleStore.class.getSimpleName();
 
-    public static ArrayList<Locale> getAppSupportedLocales(Context context, String packageName) {
+    public static AppLocaleResult getAppSupportedLocales(
+            Context context, String packageName) {
+        LocaleConfig localeConfig = null;
+        AppLocaleResult.LocaleStatus localeStatus = LocaleStatus.UNKNOWN_FAILURE;
         ArrayList<Locale> appSupportedLocales = new ArrayList<>();
-        LocaleList packageLocaleList = getPackageLocales(context, packageName);
 
-        if (packageLocaleList != null && packageLocaleList.size() > 0) {
-            for (int i = 0; i < packageLocaleList.size(); i++) {
-                appSupportedLocales.add(packageLocaleList.get(i));
-            }
-            Log.d(TAG, "getAppSupportedLocales from LocaleConfig. Size: "
-                    + appSupportedLocales.size());
-        } else {
-            String[] languages = getAssetLocales(context, packageName);
-            for (String language : languages) {
-                appSupportedLocales.add(Locale.forLanguageTag(language));
-            }
-            Log.d(TAG, "getAppSupportedLocales from asset. Size: "
-                    + appSupportedLocales.size());
-        }
-        return appSupportedLocales;
-    }
-
-    private static LocaleList getPackageLocales(Context context, String packageName) {
         try {
-            LocaleConfig localeConfig =
-                    new LocaleConfig(context.createPackageContext(packageName, 0));
-            if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) {
-                return localeConfig.getSupportedLocales();
-            }
+            localeConfig = new LocaleConfig(context.createPackageContext(packageName, 0));
         } catch (PackageManager.NameNotFoundException e) {
             Log.w(TAG, "Can not found the package name : " + packageName + " / " + e);
         }
-        return null;
+
+        if (localeConfig != null) {
+            if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) {
+                LocaleList packageLocaleList = localeConfig.getSupportedLocales();
+                if (packageLocaleList.size() > 0) {
+                    localeStatus = LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG;
+                    for (int i = 0; i < packageLocaleList.size(); i++) {
+                        appSupportedLocales.add(packageLocaleList.get(i));
+                    }
+                } else {
+                    localeStatus = LocaleStatus.NO_SUPPORTED_LANGUAGE;
+                }
+            } else if (localeConfig.getStatus() == LocaleConfig.STATUS_NOT_SPECIFIED) {
+                localeStatus = LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET;
+                String[] languages = getAssetLocales(context, packageName);
+                for (String language : languages) {
+                    appSupportedLocales.add(Locale.forLanguageTag(language));
+                }
+            }
+        }
+        Log.d(TAG, "getAppSupportedLocales(). status: " + localeStatus
+                + ", appSupportedLocales:" + appSupportedLocales.size());
+        return new AppLocaleResult(localeStatus, appSupportedLocales);
     }
 
     private static String[] getAssetLocales(Context context, String packageName) {
@@ -82,4 +86,20 @@
         return new String[0];
     }
 
+    static class AppLocaleResult {
+        enum LocaleStatus {
+            UNKNOWN_FAILURE,
+            NO_SUPPORTED_LANGUAGE,
+            GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG,
+            GET_SUPPORTED_LANGUAGE_FROM_ASSET,
+        }
+
+        LocaleStatus mLocaleStatus;
+        ArrayList<Locale> mAppSupportedLocales;
+
+        public AppLocaleResult(LocaleStatus localeStatus, ArrayList<Locale> appSupportedLocales) {
+            this.mLocaleStatus = localeStatus;
+            this.mAppSupportedLocales = appSupportedLocales;
+        }
+    }
 }
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index 52c74cf..213af26 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.app;
 
+import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus;
+
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
 import android.app.ListFragment;
@@ -158,30 +160,39 @@
             if (appCurrentLocale != null && !isForCountryMode) {
                 mLocaleList.add(appCurrentLocale);
             }
-            filterTheLanguagesNotSupportedInApp(context, appPackageName);
 
-            if (!isForCountryMode) {
-                mLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo());
+            AppLocaleStore.AppLocaleResult result =
+                    AppLocaleStore.getAppSupportedLocales(context, appPackageName);
+            boolean shouldShowList =
+                    result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG
+                    || result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET;
+
+            mLocaleList = filterTheLanguagesNotSupportedInApp(
+                    shouldShowList, result.mAppSupportedLocales);
+
+            // Add "system language"
+            if (!isForCountryMode && shouldShowList) {
+                mLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo(appCurrentLocale == null));
             }
         }
         return true;
     }
 
-    private void filterTheLanguagesNotSupportedInApp(Context context, String appPackageName) {
-        ArrayList<Locale> supportedLocales =
-                AppLocaleStore.getAppSupportedLocales(context, appPackageName);
-
+    private Set<LocaleStore.LocaleInfo> filterTheLanguagesNotSupportedInApp(
+            boolean shouldShowList, ArrayList<Locale> supportedLocales) {
         Set<LocaleStore.LocaleInfo> filteredList = new HashSet<>();
-        for(LocaleStore.LocaleInfo li: mLocaleList) {
-            for(Locale l: supportedLocales) {
-                if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) {
-                    filteredList.add(li);
+        if (shouldShowList) {
+            for(LocaleStore.LocaleInfo li: mLocaleList) {
+                for(Locale l: supportedLocales) {
+                    if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) {
+                        filteredList.add(li);
+                    }
                 }
             }
+            Log.d(TAG, "mLocaleList after app-supported filter:  " + filteredList.size());
         }
-        Log.d(TAG, "mLocaleList after app-supported filter:  " + filteredList.size());
 
-        mLocaleList = filteredList;
+        return filteredList;
     }
 
     private void returnToParentFrame() {
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index cea8eaa..eb11b9b 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -281,9 +281,12 @@
      * The "system default" is special case for per-app picker. Intentionally keep the locale
      * empty to let activity know "system default" been selected.
      */
-    public static LocaleInfo getSystemDefaultLocaleInfo() {
+    public static LocaleInfo getSystemDefaultLocaleInfo(boolean hasAppLanguage) {
         LocaleInfo systemDefaultInfo = new LocaleInfo("");
         systemDefaultInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SYSTEM_LANGUAGE;
+        if (hasAppLanguage) {
+            systemDefaultInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_CURRENT;
+        }
         systemDefaultInfo.mIsTranslated = true;
         return systemDefaultInfo;
     }
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index 2eb104e..68b8968 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -27,6 +27,7 @@
 import android.widget.BaseAdapter;
 import android.widget.Filter;
 import android.widget.Filterable;
+import android.widget.FrameLayout;
 import android.widget.TextView;
 
 import com.android.internal.R;
@@ -54,7 +55,11 @@
     private static final int TYPE_HEADER_ALL_OTHERS = 1;
     private static final int TYPE_LOCALE = 2;
     private static final int TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER = 3;
+    private static final int TYPE_CURRENT_LOCALE = 4;
     private static final int MIN_REGIONS_FOR_SUGGESTIONS = 6;
+    private static final int APP_LANGUAGE_PICKER_TYPE_COUNT = 5;
+    private static final int SYSTEM_LANGUAGE_TYPE_COUNT = 3;
+    private static final int SYSTEM_LANGUAGE_WITHOUT_HEADER_TYPE_COUNT = 1;
 
     private ArrayList<LocaleStore.LocaleInfo> mLocaleOptions;
     private ArrayList<LocaleStore.LocaleInfo> mOriginalLocaleOptions;
@@ -93,7 +98,8 @@
     @Override
     public boolean isEnabled(int position) {
         return getItemViewType(position) == TYPE_LOCALE
-                || getItemViewType(position) == TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER;
+                || getItemViewType(position) == TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER
+                || getItemViewType(position) == TYPE_CURRENT_LOCALE;
     }
 
     @Override
@@ -112,6 +118,9 @@
             if (item.isSystemLocale()) {
                 return TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER;
             }
+            if (item.isAppCurrentLocale()) {
+                return TYPE_CURRENT_LOCALE;
+            }
             return TYPE_LOCALE;
         }
     }
@@ -119,11 +128,13 @@
     @Override
     public int getViewTypeCount() {
         if (!TextUtils.isEmpty(mAppPackageName) && showHeaders()) {
-            return 4; // Two headers and 1 for "System language"
+            // Two headers, 1 "System language", 1 current locale
+            return APP_LANGUAGE_PICKER_TYPE_COUNT;
         } else if (showHeaders()) {
-            return 3; // Two headers in addition to the locales
+            // Two headers in addition to the locales
+            return SYSTEM_LANGUAGE_TYPE_COUNT;
         } else {
-            return 1; // Locales items only
+            return SYSTEM_LANGUAGE_WITHOUT_HEADER_TYPE_COUNT; // Locales items only
         }
     }
 
@@ -204,11 +215,15 @@
                 textView.setTextLocale(
                         mDisplayLocale != null ? mDisplayLocale : Locale.getDefault());
                 break;
-
             case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER:
                 if (!(convertView instanceof ViewGroup)) {
-                    convertView = mInflater.inflate(
-                            R.layout.app_language_picker_system_default, parent, false);
+                    if (((LocaleStore.LocaleInfo)getItem(position)).isAppCurrentLocale()) {
+                        convertView = mInflater.inflate(
+                                R.layout.app_language_picker_system_current, parent, false);
+                    } else {
+                        convertView = mInflater.inflate(
+                                R.layout.app_language_picker_system_default, parent, false);
+                    }
                 }
 
                 Locale defaultLocale = Locale.getDefault();
@@ -219,25 +234,20 @@
                 subtitle.setText(defaultLocale.getDisplayName());
                 subtitle.setTextLocale(defaultLocale);
                 break;
+            case TYPE_CURRENT_LOCALE:
+                if (!(convertView instanceof ViewGroup)) {
+                    convertView = mInflater.inflate(
+                            R.layout.app_language_picker_current_locale_item, parent, false);
+                }
+                updateTextView(
+                        convertView, convertView.findViewById(R.id.language_picker_item), position);
+                break;
             default:
                 // Covers both null, and "reusing" a wrong kind of view
                 if (!(convertView instanceof ViewGroup)) {
                     convertView = mInflater.inflate(R.layout.language_picker_item, parent, false);
                 }
-
-                TextView text = (TextView) convertView.findViewById(R.id.locale);
-                LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position);
-                text.setText(item.getLabel(mCountryMode));
-                text.setTextLocale(item.getLocale());
-                text.setContentDescription(item.getContentDescription(mCountryMode));
-                if (mCountryMode) {
-                    int layoutDir = TextUtils.getLayoutDirectionFromLocale(item.getParent());
-                    //noinspection ResourceType
-                    convertView.setLayoutDirection(layoutDir);
-                    text.setTextDirection(layoutDir == View.LAYOUT_DIRECTION_RTL
-                            ? View.TEXT_DIRECTION_RTL
-                            : View.TEXT_DIRECTION_LTR);
-                }
+                updateTextView(convertView, convertView.findViewById(R.id.locale), position);
         }
         return convertView;
     }
@@ -348,4 +358,19 @@
     public Filter getFilter() {
         return new FilterByNativeAndUiNames();
     }
+
+    private void updateTextView(View convertView, TextView text, int position) {
+        LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position);
+        text.setText(item.getLabel(mCountryMode));
+        text.setTextLocale(item.getLocale());
+        text.setContentDescription(item.getContentDescription(mCountryMode));
+        if (mCountryMode) {
+            int layoutDir = TextUtils.getLayoutDirectionFromLocale(item.getParent());
+            //noinspection ResourceType
+            convertView.setLayoutDirection(layoutDir);
+            text.setTextDirection(layoutDir == View.LAYOUT_DIRECTION_RTL
+                    ? View.TEXT_DIRECTION_RTL
+                    : View.TEXT_DIRECTION_LTR);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 576860d..b334e91 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -22,12 +22,17 @@
 import android.net.PlatformVpnProfile;
 import android.net.ProxyInfo;
 import android.net.Uri;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtils;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.HexDump;
 import com.android.net.module.util.ProxyUtils;
 
 import java.io.UnsupportedEncodingException;
@@ -69,7 +74,8 @@
     public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6;
     public static final int TYPE_IKEV2_IPSEC_PSK = 7;
     public static final int TYPE_IKEV2_IPSEC_RSA = 8;
-    public static final int TYPE_MAX = 8;
+    public static final int TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS = 9;
+    public static final int TYPE_MAX = 9;
 
     // Match these constants with R.array.vpn_proxy_settings.
     public static final int PROXY_NONE = 0;
@@ -145,25 +151,27 @@
 
     public final boolean excludeLocalRoutes;                     // 25
     public final boolean requiresInternetValidation;             // 26
+    public final IkeTunnelConnectionParams ikeTunConnParams;     // 27
 
     // Helper fields.
     @UnsupportedAppUsage
     public transient boolean saveLogin = false;
 
     public VpnProfile(String key) {
-        this(key, false, false, false);
+        this(key, false, false, false, null);
     }
 
     public VpnProfile(String key, boolean isRestrictedToTestNetworks) {
-        this(key, isRestrictedToTestNetworks, false, false);
+        this(key, isRestrictedToTestNetworks, false, false, null);
     }
 
     public VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes,
-            boolean requiresInternetValidation) {
+            boolean requiresInternetValidation, IkeTunnelConnectionParams ikeTunConnParams) {
         this.key = key;
         this.isRestrictedToTestNetworks = isRestrictedToTestNetworks;
         this.excludeLocalRoutes = excludeLocalRoutes;
         this.requiresInternetValidation = requiresInternetValidation;
+        this.ikeTunConnParams = ikeTunConnParams;
     }
 
     @UnsupportedAppUsage
@@ -195,6 +203,10 @@
         isRestrictedToTestNetworks = in.readBoolean();
         excludeLocalRoutes = in.readBoolean();
         requiresInternetValidation = in.readBoolean();
+        final PersistableBundle bundle =
+                in.readParcelable(PersistableBundle.class.getClassLoader());
+        ikeTunConnParams = (bundle == null) ? null
+                : TunnelConnectionParamsUtils.fromPersistableBundle(bundle);
     }
 
     /**
@@ -244,6 +256,8 @@
         out.writeBoolean(isRestrictedToTestNetworks);
         out.writeBoolean(excludeLocalRoutes);
         out.writeBoolean(requiresInternetValidation);
+        out.writeParcelable(ikeTunConnParams == null ? null
+                : TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams), flags);
     }
 
     /**
@@ -259,15 +273,17 @@
             }
 
             String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1);
+
             // Acceptable numbers of values are:
             // 14-19: Standard profile, with option for serverCert, proxy
             // 24: Standard profile with serverCert, proxy and platform-VPN parameters
             // 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks
             // 26:                                            ...and excludeLocalRoutes
-            //     (26 can only be found on dogfood devices)
             // 27:                                            ...and requiresInternetValidation
+            //     (26,27 can only be found on dogfood devices)
+            // 28:                                            ...and ikeTunConnParams
             if ((values.length < 14 || (values.length > 19 && values.length < 24)
-                    || values.length > 27)) {
+                    || values.length > 28)) {
                 return null;
             }
 
@@ -292,8 +308,22 @@
                 requiresInternetValidation = false;
             }
 
+            final IkeTunnelConnectionParams tempIkeTunConnParams;
+            // Assign null directly if the ikeTunConParams field is empty.
+            if (values.length >= 28 && values[27].length() != 0) {
+                final Parcel parcel = Parcel.obtain();
+                final byte[] bytes = HexDump.hexStringToByteArray(values[27]);
+                parcel.unmarshall(bytes, 0, bytes.length);
+                parcel.setDataPosition(0);
+                final PersistableBundle bundle = (PersistableBundle) parcel.readValue(
+                        PersistableBundle.class.getClassLoader());
+                tempIkeTunConnParams = TunnelConnectionParamsUtils.fromPersistableBundle(bundle);
+            } else {
+                tempIkeTunConnParams = null;
+            }
+
             VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks,
-                    excludeLocalRoutes, requiresInternetValidation);
+                    excludeLocalRoutes, requiresInternetValidation, tempIkeTunConnParams);
             profile.name = values[0];
             profile.type = Integer.parseInt(values[1]);
             if (profile.type < 0 || profile.type > TYPE_MAX) {
@@ -345,6 +375,7 @@
             profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
             return profile;
         } catch (Exception e) {
+            Log.d(TAG, "Got exception in decode.", e);
             // ignore
         }
         return null;
@@ -406,6 +437,17 @@
         builder.append(VALUE_DELIMITER).append(excludeLocalRoutes);
         builder.append(VALUE_DELIMITER).append(requiresInternetValidation);
 
+        if (ikeTunConnParams != null) {
+            final PersistableBundle bundle =
+                    TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams);
+            final Parcel parcel = Parcel.obtain();
+            parcel.writeValue(bundle);
+            final byte[] bytes = parcel.marshall();
+            builder.append(VALUE_DELIMITER).append(HexDump.toHexString(bytes));
+        } else {
+            builder.append(VALUE_DELIMITER).append("");
+        }
+
         return builder.toString().getBytes(StandardCharsets.UTF_8);
     }
 
@@ -486,7 +528,8 @@
             key, type, server, username, password, dnsServers, searchDomains, routes, mppe,
             l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert,
             proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline,
-            isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation);
+            isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation,
+            ikeTunConnParams);
     }
 
     /** Checks VPN profiles for interior equality. */
@@ -521,7 +564,8 @@
                 && areAuthParamsInline == other.areAuthParamsInline
                 && isRestrictedToTestNetworks == other.isRestrictedToTestNetworks
                 && excludeLocalRoutes == other.excludeLocalRoutes
-                && requiresInternetValidation == other.requiresInternetValidation;
+                && requiresInternetValidation == other.requiresInternetValidation
+                && Objects.equals(ikeTunConnParams, other.ikeTunConnParams);
     }
 
     @NonNull
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index c825f77..06d12b5 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -59,7 +59,6 @@
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -1487,7 +1486,7 @@
             addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0);
         }
 
-        if (isFilesystemSupported("erofs")) {
+        if (isErofsSupported()) {
             if (isKernelVersionAtLeast(5, 10)) {
                 addFeature(PackageManager.FEATURE_EROFS, 0);
             } else if (isKernelVersionAtLeast(4, 19)) {
@@ -1865,11 +1864,10 @@
         return Process.myUid() == Process.SYSTEM_UID;
     }
 
-    private static boolean isFilesystemSupported(String fs) {
+    private static boolean isErofsSupported() {
         try {
-            final byte[] fsTableData = Files.readAllBytes(Paths.get("/proc/filesystems"));
-            final String fsTable = new String(fsTableData, StandardCharsets.UTF_8);
-            return fsTable.contains("\t" + fs + "\n");
+            final Path path = Paths.get("/sys/fs/erofs");
+            return Files.exists(path);
         } catch (Exception e) {
             return false;
         }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 96fe7e1..4075c5f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2086,7 +2086,7 @@
 
     <!-- @SystemApi @hide Allows applications to register network factory or agent -->
     <permission android:name="android.permission.NETWORK_FACTORY"
-                android:protectionLevel="signature" />
+                android:protectionLevel="signature|role" />
 
     <!-- @SystemApi @hide Allows applications to access network stats provider -->
     <permission android:name="android.permission.NETWORK_STATS_PROVIDER"
@@ -2275,13 +2275,13 @@
          @hide
     -->
     <permission android:name="android.permission.BLUETOOTH_MAP"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- Allows bluetooth stack to access files
          @hide This should only be used by Bluetooth apk.
     -->
     <permission android:name="android.permission.BLUETOOTH_STACK"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- Allows uhid write access for creating virtual input devices
          @hide
@@ -2552,7 +2552,7 @@
     <!-- Allows access to configure network interfaces, configure/use IPSec, etc.
          @hide -->
     <permission android:name="android.permission.NET_ADMIN"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- Allows registration for remote audio playback. @hide -->
     <permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK"
@@ -2676,7 +2676,7 @@
     <!-- Allows listen permission to always reported system signal strength.
          @hide Used internally. -->
     <permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- @SystemApi Protects the ability to register any PhoneAccount with
          PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. This capability indicates that the PhoneAccount
@@ -3911,7 +3911,7 @@
          Not for use by third party apps.
          @hide -->
     <permission android:name="android.permission.MANAGE_APP_OPS_MODES"
-        android:protectionLevel="signature|installer|verifier" />
+        android:protectionLevel="signature|installer|verifier|role" />
 
     <!-- @SystemApi Allows an application to open windows that are for use by parts
          of the system user interface.
@@ -4792,7 +4792,7 @@
     <!-- Allows an application to manage the companion devices.
          @hide -->
     <permission android:name="android.permission.MANAGE_COMPANION_DEVICES"
-                android:protectionLevel="signature" />
+                android:protectionLevel="signature|role" />
 
     <!-- Allows an application to subscribe to notifications about the presence status change
          of their associated companion device
@@ -5041,7 +5041,7 @@
     <!-- @TestApi Allows an application to query audio related state.
          @hide -->
     <permission android:name="android.permission.QUERY_AUDIO_STATE"
-                android:protectionLevel="signature" />
+                android:protectionLevel="signature|role" />
 
     <!-- Allows an application to modify what effects are applied to all audio
          (matching certain criteria) from any application.
@@ -5114,7 +5114,7 @@
         @hide
     -->
    <permission android:name="android.permission.DEVICE_POWER"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- Allows toggling battery saver on the system.
          Superseded by DEVICE_POWER permission. @hide @SystemApi
@@ -5140,7 +5140,7 @@
 
    <!-- @hide Allows low-level access to tun tap driver -->
     <permission android:name="android.permission.NET_TUNNELING"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- Run as a manufacturer test application, running as the root user.
          Only available when the device is running in manufacturer test mode.
diff --git a/core/res/res/drawable/ic_check_24dp.xml b/core/res/res/drawable/ic_check_24dp.xml
new file mode 100644
index 0000000..a0e21ff
--- /dev/null
+++ b/core/res/res/drawable/ic_check_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ 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="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"
+        android:fillColor="@android:color/white"/>
+</vector>
diff --git a/core/res/res/layout/app_language_picker_current_locale_item.xml b/core/res/res/layout/app_language_picker_current_locale_item.xml
new file mode 100644
index 0000000..bf6d963
--- /dev/null
+++ b/core/res/res/layout/app_language_picker_current_locale_item.xml
@@ -0,0 +1,44 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight=".8">
+        <include
+            android:id="@+id/language_picker_item"
+            layout="@layout/language_picker_item" />
+    </FrameLayout>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight=".2"
+        android:gravity="center"
+        android:minHeight="?android:attr/listPreferredItemHeight">
+        <ImageView
+            android:id="@+id/imageView"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:src="@drawable/ic_check_24dp"
+            app:tint="#0F9D58"/>
+    </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/app_language_picker_system_current.xml b/core/res/res/layout/app_language_picker_system_current.xml
new file mode 100644
index 0000000..341ee25
--- /dev/null
+++ b/core/res/res/layout/app_language_picker_system_current.xml
@@ -0,0 +1,45 @@
+<!--
+  ~ 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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight=".8">
+        <include
+            android:id="@+id/system_language_view"
+            layout="@layout/app_language_picker_system_default" />
+    </FrameLayout>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight=".2"
+        android:gravity="center"
+        android:minHeight="?android:attr/listPreferredItemHeight">
+        <ImageView
+            android:id="@+id/imageView"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:src="@drawable/ic_check_24dp"
+            app:tint="#0F9D58"/>
+    </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 783fabe..f5c82d5 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -35,4 +35,9 @@
     <color name="personal_apps_suspension_notification_color">#8AB4F8</color>
 
     <color name="overview_background">@color/overview_background_dark</color>
+
+    <color name="user_icon_4">#fff439a0</color><!-- pink -->
+    <color name="user_icon_6">#ff4ecde6</color><!-- cyan -->
+    <color name="user_icon_7">#fffbbc04</color><!-- yellow -->
+    <color name="user_icon_8">#fffa903e</color><!-- orange -->
 </resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 54325e5..71c98d0 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -174,14 +174,14 @@
     <color name="system_notification_accent_color">#00000000</color>
 
     <!-- Default user icon colors -->
-    <color name="user_icon_1">#ff00bcd4</color><!-- cyan 500 -->
-    <color name="user_icon_2">#ff3f51b5</color><!-- indigo 500 -->
-    <color name="user_icon_3">#ff4285f4</color><!-- blue 500 -->
-    <color name="user_icon_4">#ffe91e63</color><!-- pink 500 -->
-    <color name="user_icon_5">#ff0f9d58</color><!-- green 500 -->
-    <color name="user_icon_6">#ff8bc34a</color><!-- light green 500 -->
-    <color name="user_icon_7">#ffff9800</color><!-- orange 500 -->
-    <color name="user_icon_8">#ffff5722</color><!-- deep orange 500 -->
+    <color name="user_icon_1">#ffe46962</color><!-- red -->
+    <color name="user_icon_2">#ffaf5cf7</color><!-- purple -->
+    <color name="user_icon_3">#ff4c8df6</color><!-- blue -->
+    <color name="user_icon_4">#fff439a0</color><!-- pink -->
+    <color name="user_icon_5">#ff1ea446</color><!-- green -->
+    <color name="user_icon_6">#ff129eaf</color><!-- cyan -->
+    <color name="user_icon_7">#ffb26c00</color><!-- yellow -->
+    <color name="user_icon_8">#ffe8710a</color><!-- orange -->
     <color name="user_icon_default_gray">#ff9e9e9e</color><!-- gray 500 -->
     <color name="user_icon_default_white">#ffffffff</color><!-- white -->
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 60d875c..42f789b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2918,6 +2918,11 @@
         com.android.settings.intelligence
     </string>
 
+    <!-- System bluetooth stack package name -->
+    <string name="config_systemBluetoothStack" translatable="false">
+        com.android.bluetooth.services
+    </string>
+
     <!-- Flag indicating that the media framework should not allow changes or mute on any
          stream or global volumes. -->
     <bool name="config_useFixedVolume">false</bool>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 870f549..86bad7f 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -184,6 +184,8 @@
     <public name="safety_protection_display_text" />
     <!-- @hide @SystemApi -->
     <public name="config_systemSettingsIntelligence" />
+    <!-- @hide -->
+    <public name="config_systemBluetoothStack" />
   </staging-public-group>
 
   <staging-public-group type="dimen" first-id="0x01db0000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dd69fa0..776d3da 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4754,7 +4754,11 @@
   <java-symbol type="drawable" name="ic_swap_horiz" />
   <java-symbol type="bool" name="config_notificationForceUserSetOnUpgrade" />
 
+  <!-- For app language picker -->
   <java-symbol type="string" name="system_locale_title" />
   <java-symbol type="layout" name="app_language_picker_system_default" />
+  <java-symbol type="layout" name="app_language_picker_system_current" />
+  <java-symbol type="layout" name="app_language_picker_current_locale_item" />
   <java-symbol type="id" name="system_locale_subtitle" />
+  <java-symbol type="id" name="language_picker_item" />
 </resources>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 58a2073..04ead1b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -33,6 +33,30 @@
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.bluetooth.services">
+        <permission name="android.permission.DUMP"/>
+        <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.TETHER_PRIVILEGED"/>
+        <permission name="android.permission.CALL_PRIVILEGED"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+        <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+        <permission name="android.permission.UPDATE_DEVICE_STATS"/>
+        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+        <permission name="android.permission.NFC_HANDOVER_STATUS"/>
+        <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+        <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
+        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+        <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+        <permission name="android.permission.REAL_GET_TASKS"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
+        <permission name="android.permission.WRITE_APN_SETTINGS"/>
+        <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+    </privapp-permissions>
+
     <privapp-permissions package="com.android.backupconfirm">
         <permission name="android.permission.BACKUP"/>
         <permission name="android.permission.CRYPT_KEEPER"/>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index e528df8..3f0b01b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -131,7 +131,7 @@
                 mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
                 mRootTaskInfo.configuration, this /* layoutChangeListener */,
                 mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer(),
-                true /* applyDismissingParallax */);
+                SplitLayout.PARALLAX_DISMISSING);
         mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout);
 
         final WindowContainerToken token1 = task1.token;
@@ -327,13 +327,15 @@
     @Override
     public void onLayoutPositionChanging(SplitLayout layout) {
         mSyncQueue.runInSync(t ->
-                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
+                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
+                        true /* applyResizingOffset */));
     }
 
     @Override
     public void onLayoutSizeChanging(SplitLayout layout) {
         mSyncQueue.runInSync(t ->
-                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
+                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
+                        true /* applyResizingOffset */));
     }
 
     @Override
@@ -342,7 +344,8 @@
         layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2);
         mSyncQueue.queue(wct);
         mSyncQueue.runInSync(t ->
-                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
+                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
+                        false /* applyResizingOffset */));
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 577ced5..42ac195 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -48,18 +48,16 @@
  * Controls the window animation run when a user initiates a back gesture.
  */
 public class BackAnimationController implements RemoteCallable<BackAnimationController> {
-
-    private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP =
-            "persist.debug.back_predictability_progress_threshold";
-    // By default, enable new back dispatching without any animations.
-    private static final int BACK_PREDICTABILITY_PROP =
-            SystemProperties.getInt("persist.debug.back_predictability", 1);
-    public static final boolean IS_ENABLED = BACK_PREDICTABILITY_PROP > 0;
-    private static final int PROGRESS_THRESHOLD = SystemProperties
-            .getInt(BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP, -1);
     private static final String TAG = "BackAnimationController";
+    private static final String PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP =
+            "persist.wm.debug.predictive_back_progress_threshold";
+    public static final boolean IS_ENABLED =
+            SystemProperties.getInt("persist.wm.debug.predictive_back", 1) != 0;
+    private static final int PROGRESS_THRESHOLD = SystemProperties
+            .getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1);
     @VisibleForTesting
-    boolean mEnableAnimations = (BACK_PREDICTABILITY_PROP & (1 << 1)) != 0;
+    boolean mEnableAnimations = SystemProperties.getInt(
+            "persist.wm.debug.predictive_back_anim", 0) != 0;
 
     /**
      * Location of the initial touch event of the back gesture.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 116d352..ec81d23 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -72,6 +72,10 @@
  */
 public final class SplitLayout implements DisplayInsetsController.OnInsetsChangedListener {
 
+    public static final int PARALLAX_NONE = 0;
+    public static final int PARALLAX_DISMISSING = 1;
+    public static final int PARALLAX_ALIGN_CENTER = 2;
+
     private final int mDividerWindowWidth;
     private final int mDividerInsets;
     private final int mDividerSize;
@@ -87,7 +91,7 @@
     private final SplitWindowManager mSplitWindowManager;
     private final DisplayImeController mDisplayImeController;
     private final ImePositionProcessor mImePositionProcessor;
-    private final DismissingEffectPolicy mDismissingEffectPolicy;
+    private final ResizingEffectPolicy mSurfaceEffectPolicy;
     private final ShellTaskOrganizer mTaskOrganizer;
     private final InsetsState mInsetsState = new InsetsState();
 
@@ -105,7 +109,7 @@
             SplitLayoutHandler splitLayoutHandler,
             SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
             DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer,
-            boolean applyDismissingParallax) {
+            int parallaxType) {
         mContext = context.createConfigurationContext(configuration);
         mOrientation = configuration.orientation;
         mRotation = configuration.windowConfiguration.getRotation();
@@ -115,7 +119,7 @@
                 parentContainerCallbacks);
         mTaskOrganizer = taskOrganizer;
         mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
-        mDismissingEffectPolicy = new DismissingEffectPolicy(applyDismissingParallax);
+        mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType);
 
         final Resources resources = context.getResources();
         mDividerSize = resources.getDimensionPixelSize(R.dimen.split_divider_bar_width);
@@ -281,7 +285,7 @@
         }
         DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */);
         DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */);
-        mDismissingEffectPolicy.applyDividerPosition(position, isLandscape);
+        mSurfaceEffectPolicy.applyDividerPosition(position, isLandscape);
     }
 
     /** Inflates {@link DividerView} on the root surface. */
@@ -486,7 +490,8 @@
 
     /** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */
     public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1,
-            SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
+            SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2,
+            boolean applyResizingOffset) {
         final SurfaceControl dividerLeash = getDividerLeash();
         if (dividerLeash != null) {
             mTempRect.set(getRefDividerBounds());
@@ -506,7 +511,10 @@
             return;
         }
 
-        mDismissingEffectPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2);
+        mSurfaceEffectPolicy.adjustDimSurface(t, dimLayer1, dimLayer2);
+        if (applyResizingOffset) {
+            mSurfaceEffectPolicy.adjustRootSurface(t, leash1, leash2);
+        }
     }
 
     /** Apply recorded task layout to the {@link WindowContainerTransaction}. */
@@ -590,7 +598,7 @@
          * Calls when resizing the split bounds.
          *
          * @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl,
-         * SurfaceControl, SurfaceControl)
+         * SurfaceControl, SurfaceControl, boolean)
          */
         void onLayoutSizeChanging(SplitLayout layout);
 
@@ -600,7 +608,7 @@
          * @see #applyTaskChanges(WindowContainerTransaction, ActivityManager.RunningTaskInfo,
          * ActivityManager.RunningTaskInfo)
          * @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl,
-         * SurfaceControl, SurfaceControl)
+         * SurfaceControl, SurfaceControl, boolean)
          */
         void onLayoutSizeChanged(SplitLayout layout);
 
@@ -609,7 +617,7 @@
          * panel.
          *
          * @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl,
-         * SurfaceControl, SurfaceControl)
+         * SurfaceControl, SurfaceControl, boolean)
          */
         void onLayoutPositionChanging(SplitLayout layout);
 
@@ -637,21 +645,25 @@
      * Calculates and applies proper dismissing parallax offset and dimming value to hint users
      * dismissing gesture.
      */
-    private class DismissingEffectPolicy {
+    private class ResizingEffectPolicy {
         /** Indicates whether to offset splitting bounds to hint dismissing progress or not. */
-        private final boolean mApplyParallax;
+        private final int mParallaxType;
+
+        int mShrinkSide = DOCKED_INVALID;
 
         // The current dismissing side.
         int mDismissingSide = DOCKED_INVALID;
 
         // The parallax offset to hint the dismissing side and progress.
-        final Point mDismissingParallaxOffset = new Point();
+        final Point mParallaxOffset = new Point();
 
         // The dimming value to hint the dismissing side and progress.
         float mDismissingDimValue = 0.0f;
+        final Rect mContentBounds = new Rect();
+        final Rect mSurfaceBounds = new Rect();
 
-        DismissingEffectPolicy(boolean applyDismissingParallax) {
-            mApplyParallax = applyDismissingParallax;
+        ResizingEffectPolicy(int parallaxType) {
+            mParallaxType = parallaxType;
         }
 
         /**
@@ -662,7 +674,7 @@
          */
         void applyDividerPosition(int position, boolean isLandscape) {
             mDismissingSide = DOCKED_INVALID;
-            mDismissingParallaxOffset.set(0, 0);
+            mParallaxOffset.set(0, 0);
             mDismissingDimValue = 0;
 
             int totalDismissingDistance = 0;
@@ -676,15 +688,39 @@
                         - mDividerSnapAlgorithm.getDismissEndTarget().position;
             }
 
+            final boolean topLeftShrink = isLandscape
+                    ? position < mWinBounds1.right : position < mWinBounds1.bottom;
+            if (topLeftShrink) {
+                mShrinkSide = isLandscape ? DOCKED_LEFT : DOCKED_TOP;
+                mContentBounds.set(mWinBounds1);
+                mSurfaceBounds.set(mBounds1);
+            } else {
+                mShrinkSide = isLandscape ? DOCKED_RIGHT : DOCKED_BOTTOM;
+                mContentBounds.set(mWinBounds2);
+                mSurfaceBounds.set(mBounds2);
+            }
+
             if (mDismissingSide != DOCKED_INVALID) {
                 float fraction = Math.max(0,
                         Math.min(mDividerSnapAlgorithm.calculateDismissingFraction(position), 1f));
                 mDismissingDimValue = DIM_INTERPOLATOR.getInterpolation(fraction);
-                fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
+                if (mParallaxType == PARALLAX_DISMISSING) {
+                    fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
+                    if (isLandscape) {
+                        mParallaxOffset.x = (int) (fraction * totalDismissingDistance);
+                    } else {
+                        mParallaxOffset.y = (int) (fraction * totalDismissingDistance);
+                    }
+                }
+            }
+
+            if (mParallaxType == PARALLAX_ALIGN_CENTER) {
                 if (isLandscape) {
-                    mDismissingParallaxOffset.x = (int) (fraction * totalDismissingDistance);
+                    mParallaxOffset.x =
+                            (mSurfaceBounds.width() - mContentBounds.width()) / 2;
                 } else {
-                    mDismissingParallaxOffset.y = (int) (fraction * totalDismissingDistance);
+                    mParallaxOffset.y =
+                            (mSurfaceBounds.height() - mContentBounds.height()) / 2;
                 }
             }
         }
@@ -704,41 +740,66 @@
         }
 
         /** Applies parallax offset and dimming value to the root surface at the dismissing side. */
-        boolean adjustDismissingSurface(SurfaceControl.Transaction t,
-                SurfaceControl leash1, SurfaceControl leash2,
+        void adjustRootSurface(SurfaceControl.Transaction t,
+                SurfaceControl leash1, SurfaceControl leash2) {
+            SurfaceControl targetLeash = null;
+
+            if (mParallaxType == PARALLAX_DISMISSING) {
+                switch (mDismissingSide) {
+                    case DOCKED_TOP:
+                    case DOCKED_LEFT:
+                        targetLeash = leash1;
+                        mTempRect.set(mBounds1);
+                        break;
+                    case DOCKED_BOTTOM:
+                    case DOCKED_RIGHT:
+                        targetLeash = leash2;
+                        mTempRect.set(mBounds2);
+                        break;
+                }
+            } else if (mParallaxType == PARALLAX_ALIGN_CENTER) {
+                switch (mShrinkSide) {
+                    case DOCKED_TOP:
+                    case DOCKED_LEFT:
+                        targetLeash = leash1;
+                        mTempRect.set(mBounds1);
+                        break;
+                    case DOCKED_BOTTOM:
+                    case DOCKED_RIGHT:
+                        targetLeash = leash2;
+                        mTempRect.set(mBounds2);
+                        break;
+                }
+            }
+            if (mParallaxType != PARALLAX_NONE && targetLeash != null) {
+                t.setPosition(targetLeash,
+                        mTempRect.left + mParallaxOffset.x, mTempRect.top + mParallaxOffset.y);
+                // Transform the screen-based split bounds to surface-based crop bounds.
+                mTempRect.offsetTo(-mParallaxOffset.x, -mParallaxOffset.y);
+                t.setWindowCrop(targetLeash, mTempRect);
+            }
+        }
+
+        void adjustDimSurface(SurfaceControl.Transaction t,
                 SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
-            SurfaceControl targetLeash, targetDimLayer;
+            SurfaceControl targetDimLayer;
             switch (mDismissingSide) {
                 case DOCKED_TOP:
                 case DOCKED_LEFT:
-                    targetLeash = leash1;
                     targetDimLayer = dimLayer1;
-                    mTempRect.set(mBounds1);
                     break;
                 case DOCKED_BOTTOM:
                 case DOCKED_RIGHT:
-                    targetLeash = leash2;
                     targetDimLayer = dimLayer2;
-                    mTempRect.set(mBounds2);
                     break;
                 case DOCKED_INVALID:
                 default:
                     t.setAlpha(dimLayer1, 0).hide(dimLayer1);
                     t.setAlpha(dimLayer2, 0).hide(dimLayer2);
-                    return false;
-            }
-
-            if (mApplyParallax) {
-                t.setPosition(targetLeash,
-                        mTempRect.left + mDismissingParallaxOffset.x,
-                        mTempRect.top + mDismissingParallaxOffset.y);
-                // Transform the screen-based split bounds to surface-based crop bounds.
-                mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y);
-                t.setWindowCrop(targetLeash, mTempRect);
+                    return;
             }
             t.setAlpha(targetDimLayer, mDismissingDimValue)
                     .setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
-            return true;
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt
index 9ede443..239ea38e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt
@@ -363,48 +363,54 @@
 
     private fun getNearbyStashedPosition(bounds: Rect, keepClearAreas: Set<Rect>): Rect {
         val screenBounds = transformedScreenBounds
-        val stashCandidates = Array(2) { Rect(bounds) }
+        val stashCandidates = mutableListOf<Rect>()
         val areasOverlappingPipX = keepClearAreas.filter { it.intersectsX(bounds) }
         val areasOverlappingPipY = keepClearAreas.filter { it.intersectsY(bounds) }
 
         if (screenBounds.bottom - bounds.bottom <= bounds.top - screenBounds.top) {
-            // bottom is closer than top, stash downwards
             val fullStashTop = screenBounds.bottom - stashOffset
 
             val maxBottom = areasOverlappingPipX.maxByOrNull { it.bottom }!!.bottom
             val partialStashTop = maxBottom + pipAreaPadding
 
-            val downPosition = stashCandidates[0]
+            val downPosition = Rect(bounds)
             downPosition.offsetTo(bounds.left, min(fullStashTop, partialStashTop))
-        } else {
-            // top is closer than bottom, stash upwards
-            val fullStashY = screenBounds.top - bounds.height() + stashOffset
+            stashCandidates += downPosition
+        }
+        if (screenBounds.bottom - bounds.bottom >= bounds.top - screenBounds.top) {
+            val fullStashBottom = screenBounds.top - bounds.height() + stashOffset
 
             val minTop = areasOverlappingPipX.minByOrNull { it.top }!!.top
-            val partialStashY = minTop - bounds.height() - pipAreaPadding
+            val partialStashBottom = minTop - bounds.height() - pipAreaPadding
 
-            val upPosition = stashCandidates[0]
-            upPosition.offsetTo(bounds.left, max(fullStashY, partialStashY))
+            val upPosition = Rect(bounds)
+            upPosition.offsetTo(bounds.left, max(fullStashBottom, partialStashBottom))
+            stashCandidates += upPosition
         }
 
         if (screenBounds.right - bounds.right <= bounds.left - screenBounds.left) {
-            // right is closer than left, stash rightwards
-            val fullStashLeft = screenBounds.right - stashOffset
+            val fullStashRight = screenBounds.right - stashOffset
 
             val maxRight = areasOverlappingPipY.maxByOrNull { it.right }!!.right
-            val partialStashLeft = maxRight + pipAreaPadding
+            val partialStashRight = maxRight + pipAreaPadding
 
-            val rightPosition = stashCandidates[1]
-            rightPosition.offsetTo(min(fullStashLeft, partialStashLeft), bounds.top)
-        } else {
-            // left is closer than right, stash leftwards
+            val rightPosition = Rect(bounds)
+            rightPosition.offsetTo(min(fullStashRight, partialStashRight), bounds.top)
+            stashCandidates += rightPosition
+        }
+        if (screenBounds.right - bounds.right >= bounds.left - screenBounds.left) {
             val fullStashLeft = screenBounds.left - bounds.width() + stashOffset
 
             val minLeft = areasOverlappingPipY.minByOrNull { it.left }!!.left
             val partialStashLeft = minLeft - bounds.width() - pipAreaPadding
 
-            val rightPosition = stashCandidates[1]
-            rightPosition.offsetTo(max(fullStashLeft, partialStashLeft), bounds.top)
+            val leftPosition = Rect(bounds)
+            leftPosition.offsetTo(max(fullStashLeft, partialStashLeft), bounds.top)
+            stashCandidates += leftPosition
+        }
+
+        if (stashCandidates.isEmpty()) {
+            return bounds
         }
 
         return stashCandidates.minByOrNull {
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 f20870f..aec51ba 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
@@ -377,7 +377,8 @@
                     return;
                 }
 
-                mStageCoordinator.updateSurfaceBounds(null /* layout */, t);
+                mStageCoordinator.updateSurfaceBounds(null /* layout */, t,
+                        false /* applyResizingOffset */);
                 for (int i = 0; i < apps.length; ++i) {
                     if (apps[i].mode == MODE_OPENING) {
                         t.show(apps[i].leash);
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 e150cf9..45931de 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
@@ -30,6 +30,7 @@
 import static android.view.WindowManager.transitTypeToString;
 import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 
+import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -495,7 +496,8 @@
 
         // Using legacy transitions, so we can't use blast sync since it conflicts.
         mTaskOrganizer.applyTransaction(wct);
-        mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t));
+        mSyncQueue.runInSync(t ->
+                updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */));
     }
 
     /**
@@ -704,9 +706,11 @@
         mMainStage.deactivate(wct, !fromEnteringPip && mMainStage == childrenToTop);
         wct.reorder(mRootTaskInfo.token, false /* onTop */);
         mTaskOrganizer.applyTransaction(wct);
-        mSyncQueue.runInSync(t -> t
-                .setWindowCrop(mMainStage.mRootLeash, null)
-                .setWindowCrop(mSideStage.mRootLeash, null));
+        mSyncQueue.runInSync(t -> {
+            setResizingSplits(false /* resizing */);
+            t.setWindowCrop(mMainStage.mRootLeash, null)
+                    .setWindowCrop(mSideStage.mRootLeash, null);
+        });
 
         // Hide divider and reset its position.
         mSplitLayout.resetDividerPosition();
@@ -780,7 +784,7 @@
     void finishEnterSplitScreen(SurfaceControl.Transaction t) {
         mSplitLayout.init();
         setDividerVisibility(true, t);
-        updateSurfaceBounds(mSplitLayout, t);
+        updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
         setSplitsVisible(true);
         mShouldUpdateRecents = true;
         updateRecentTasksSplitPair();
@@ -925,7 +929,8 @@
         if (mSplitLayout == null) {
             mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
                     mRootTaskInfo.configuration, this, mParentContainerCallbacks,
-                    mDisplayImeController, mTaskOrganizer, false /* applyDismissingParallax */);
+                    mDisplayImeController, mTaskOrganizer,
+                    PARALLAX_ALIGN_CENTER /* parallaxType */);
             mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
         }
 
@@ -1075,7 +1080,7 @@
             prepareEnterSplitScreen(wct);
             mSyncQueue.queue(wct);
             mSyncQueue.runInSync(t -> {
-                updateSurfaceBounds(mSplitLayout, t);
+                updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
                 setDividerVisibility(true, t);
             });
         }
@@ -1094,8 +1099,6 @@
 
     @Override
     public void onSnappedToDismiss(boolean bottomOrRight) {
-        setResizingSplits(false /* resizing */);
-
         final boolean mainStageToTop =
                 bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
                         : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
@@ -1104,6 +1107,7 @@
             return;
         }
 
+        setResizingSplits(false /* resizing */);
         final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         prepareExitSplitScreen(dismissTop, wct);
@@ -1121,14 +1125,14 @@
 
     @Override
     public void onLayoutPositionChanging(SplitLayout layout) {
-        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, false /* applyResizingOffset */));
     }
 
     @Override
     public void onLayoutSizeChanging(SplitLayout layout) {
         mSyncQueue.runInSync(t -> {
             setResizingSplits(true /* resizing */);
-            updateSurfaceBounds(layout, t);
+            updateSurfaceBounds(layout, t, true /* applyResizingOffset */);
             mMainStage.onResizing(getMainStageBounds(), t);
             mSideStage.onResizing(getSideStageBounds(), t);
         });
@@ -1142,7 +1146,7 @@
         mSyncQueue.queue(wct);
         mSyncQueue.runInSync(t -> {
             setResizingSplits(false /* resizing */);
-            updateSurfaceBounds(layout, t);
+            updateSurfaceBounds(layout, t, false /* applyResizingOffset */);
             mMainStage.onResized(t);
             mSideStage.onResized(t);
         });
@@ -1174,13 +1178,15 @@
         layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
     }
 
-    void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) {
+    void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t,
+            boolean applyResizingOffset) {
         final StageTaskListener topLeftStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
         final StageTaskListener bottomRightStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
         (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
-                bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer);
+                bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer,
+                applyResizingOffset);
     }
 
     void setResizingSplits(boolean resizing) {
@@ -1220,7 +1226,6 @@
         mTaskOrganizer.applyTransaction(wct);
     }
 
-    @Override
     public void onDisplayAdded(int displayId) {
         if (displayId != DEFAULT_DISPLAY) {
             return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
index f1520ed..0717405 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
@@ -253,7 +253,8 @@
                     RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
                     IRemoteAnimationFinishedCallback finishedCallback,
                     SurfaceControl.Transaction t) {
-                mStageCoordinator.updateSurfaceBounds(null /* layout */, t);
+                mStageCoordinator.updateSurfaceBounds(null /* layout */, t,
+                        false /* applyResizingOffset */);
 
                 if (apps != null) {
                     for (int i = 0; i < apps.length; ++i) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
index 6ef20e3..ac25c75 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
@@ -265,7 +265,8 @@
         mMainStage.activate(getMainStageBounds(), wct);
         mSideStage.addTask(task, getSideStageBounds(), wct);
         mSyncQueue.queue(wct);
-        mSyncQueue.runInSync(t -> updateSurfaceBounds(null /* layout */, t));
+        mSyncQueue.runInSync(
+                t -> updateSurfaceBounds(null /* layout */, t, false /* applyResizingOffset */));
         return true;
     }
 
@@ -801,12 +802,12 @@
 
     @Override
     public void onLayoutPositionChanging(SplitLayout layout) {
-        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */));
     }
 
     @Override
     public void onLayoutSizeChanging(SplitLayout layout) {
-        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */));
         mSideStage.setOutlineVisibility(false);
     }
 
@@ -816,7 +817,7 @@
         updateWindowBounds(layout, wct);
         updateUnfoldBounds();
         mSyncQueue.queue(wct);
-        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, false /* applyResizingOffset */));
         mSideStage.setOutlineVisibility(true);
         mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
     }
@@ -840,13 +841,15 @@
         layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
     }
 
-    void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) {
+    void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t,
+            boolean applyResizingOffset) {
         final StageTaskListener topLeftStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
         final StageTaskListener bottomRightStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
         (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
-                bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer);
+                bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer,
+                applyResizingOffset);
     }
 
     @Override
@@ -882,7 +885,7 @@
         if (mSplitLayout == null) {
             mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
                     mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
-                    mDisplayImeController, mTaskOrganizer, true /* applyDismissingParallax */);
+                    mDisplayImeController, mTaskOrganizer, SplitLayout.PARALLAX_DISMISSING);
             mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
 
             if (mMainUnfoldController != null && mSideUnfoldController != null) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 8b4e1f8..f1e602f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -73,7 +73,7 @@
                 mCallbacks,
                 mDisplayImeController,
                 mTaskOrganizer,
-                false /* applyDismissingParallax */));
+                SplitLayout.PARALLAX_NONE));
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 061136c6..c571d44 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -309,7 +309,7 @@
     public void testFinishEnterSplitScreen_applySurfaceLayout() {
         mStageCoordinator.finishEnterSplitScreen(new SurfaceControl.Transaction());
 
-        verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any());
+        verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any(), eq(false));
     }
 
     private class UnfoldControllerProvider implements
diff --git a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml
index fc3ec43..fd45a16 100644
--- a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml
@@ -17,7 +17,8 @@
 
 <resources>
     <style name="SettingsSpinnerTitleBar">
-        <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="android:textSize">16sp</item>
         <item name="android:textColor">@color/settingslib_spinner_title_color</item>
         <item name="android:maxLines">1</item>
         <item name="android:ellipsize">marquee</item>
@@ -29,7 +30,8 @@
     </style>
 
     <style name="SettingsSpinnerDropdown">
-        <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="android:textSize">16sp</item>
         <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item>
         <item name="android:maxLines">1</item>
         <item name="android:ellipsize">marquee</item>
diff --git a/packages/SettingsLib/res/layout/user_preference.xml b/packages/SettingsLib/res/layout/user_preference.xml
deleted file mode 100644
index f13447a..0000000
--- a/packages/SettingsLib/res/layout/user_preference.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@android:id/widget_frame"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/user_spinner_item_height"
-        android:paddingStart="@dimen/user_spinner_padding_sides"
-        android:paddingEnd="@dimen/user_spinner_padding_sides"
-        android:orientation="horizontal" >
-
-    <ImageView
-            android:id="@android:id/icon"
-            android:layout_width="@dimen/user_icon_view_height"
-            android:layout_height="@dimen/user_icon_view_height"
-            android:layout_gravity="center"
-            android:scaleType="fitCenter" />
-
-    <TextView
-            android:id="@android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:layout_gravity="center"
-            android:labelFor="@android:id/icon"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal"
-            android:paddingStart="@dimen/user_spinner_padding"
-            android:paddingEnd="@dimen/user_spinner_padding"
-            android:textAppearance="?android:attr/textAppearanceMedium" />
-
-</LinearLayout>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
index fa4ae67..6535665 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
@@ -24,6 +24,8 @@
 
 import androidx.annotation.ChecksSdkIntAtLeast;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /* Utility class is to confirm the Wi-Fi function is available by enterprise restriction */
 public class WifiEnterpriseRestrictionUtils {
     private static final String TAG = "WifiEntResUtils";
@@ -76,6 +78,26 @@
         return true;
     }
 
+    /**
+     * Confirm Wi-Fi state is allowed to change to whether user restriction is set
+     *
+     * @param context A context
+     * @return whether the device is permitted to change Wi-Fi state
+     */
+    public static boolean isChangeWifiStateAllowed(Context context) {
+        if (!hasUserRestrictionFromT(context, UserManager.DISALLOW_CHANGE_WIFI_STATE)) return true;
+        Log.w(TAG, "WI-FI state isn't allowed to change due to user restriction.");
+        return false;
+    }
+
+    @VisibleForTesting
+    static boolean hasUserRestrictionFromT(Context context, String restrictionKey) {
+        if (!isAtLeastT()) return false;
+        final UserManager userManager = context.getSystemService(UserManager.class);
+        if (userManager == null) return false;
+        return userManager.hasUserRestriction(restrictionKey);
+    }
+
     @ChecksSdkIntAtLeast(api=Build.VERSION_CODES.TIRAMISU)
     private static boolean isAtLeastT() {
         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
index f6af09a..e9326dd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
@@ -15,8 +15,11 @@
  */
 package com.android.settingslib.wifi;
 
+import static android.os.UserManager.DISALLOW_CHANGE_WIFI_STATE;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
@@ -50,6 +53,8 @@
         mContext = spy(ApplicationProvider.getApplicationContext());
         when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
         when(mUserManager.getUserRestrictions()).thenReturn(mBundle);
+        ReflectionHelpers.setStaticField(
+                Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
     }
 
     @Test
@@ -129,4 +134,41 @@
 
         assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isTrue();
     }
+
+    @Test
+    public void isChangeWifiStateAllowed_hasDisallowRestriction_shouldReturnFalse() {
+        when(mUserManager.hasUserRestriction(DISALLOW_CHANGE_WIFI_STATE)).thenReturn(true);
+
+        assertThat(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).isFalse();
+    }
+
+    @Test
+    public void isChangeWifiStateAllowed_hasNoDisallowRestriction_shouldReturnTrue() {
+        when(mUserManager.hasUserRestriction(DISALLOW_CHANGE_WIFI_STATE)).thenReturn(false);
+
+        assertThat(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).isTrue();
+    }
+
+    @Test
+    public void hasUserRestrictionFromT_setSDKForS_shouldReturnTrue() {
+        ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.S);
+
+        assertThat(WifiEnterpriseRestrictionUtils.hasUserRestrictionFromT(mContext, "key"))
+                .isFalse();
+    }
+
+    @Test
+    public void hasUserRestrictionFromT_setSDKForT_shouldReturnHasUserRestriction() {
+        ReflectionHelpers.setStaticField(
+                Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
+        when(mUserManager.hasUserRestriction(anyString())).thenReturn(false);
+
+        assertThat(WifiEnterpriseRestrictionUtils.hasUserRestrictionFromT(mContext, "key"))
+                .isFalse();
+
+        when(mUserManager.hasUserRestriction(anyString())).thenReturn(true);
+
+        assertThat(WifiEnterpriseRestrictionUtils.hasUserRestrictionFromT(mContext, "key"))
+                .isTrue();
+    }
 }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 74b759f..f934b1f 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -432,7 +432,8 @@
                 right = windowBounds.right
             )
             val callback = this@ActivityLaunchAnimator.callback!!
-            val windowBackgroundColor = callback.getBackgroundColor(window.taskInfo)
+            val windowBackgroundColor = window.taskInfo?.let { callback.getBackgroundColor(it) }
+                    ?: window.backgroundColor
 
             // Make sure we use the modified timings when animating a dialog into an app.
             val launchAnimator = if (controller.isDialogLaunch) {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
index 2e9a16f..47c1101 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
@@ -138,7 +138,7 @@
             info: TransitionInfo,
             t: SurfaceControl.Transaction
         ): RemoteAnimationTarget {
-            return RemoteAnimationTarget(
+            val target = RemoteAnimationTarget(
                     /* taskId */ if (change.taskInfo != null) change.taskInfo!!.taskId else -1,
                     /* mode */ newModeToLegacyMode(change.mode),
                     /* leash */ createLeash(info, change, order, t),
@@ -160,6 +160,8 @@
                     /* taskInfo */ change.taskInfo,
                     /* allowEnterPip */ change.allowEnterPip,
                     /* windowType */ WindowManager.LayoutParams.INVALID_WINDOW_TYPE)
+            target.backgroundColor = change.backgroundColor
+            return target
         }
 
         /**
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
index d15b8c1..74ec7f0 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
@@ -156,17 +156,7 @@
          * Any animations already in progress continue until their natural conclusion.
          */
         fun stopAnimating(rootView: View) {
-            val listener = rootView.getTag(R.id.tag_layout_listener)
-            if (listener != null && listener is View.OnLayoutChangeListener) {
-                rootView.setTag(R.id.tag_layout_listener, null /* tag */)
-                rootView.removeOnLayoutChangeListener(listener)
-            }
-
-            if (rootView is ViewGroup) {
-                for (i in 0 until rootView.childCount) {
-                    stopAnimating(rootView.getChildAt(i))
-                }
-            }
+            recursivelyRemoveListener(rootView)
         }
 
         /**
@@ -462,6 +452,20 @@
             }
         }
 
+        private fun recursivelyRemoveListener(view: View) {
+            val listener = view.getTag(R.id.tag_layout_listener)
+            if (listener != null && listener is View.OnLayoutChangeListener) {
+                view.setTag(R.id.tag_layout_listener, null /* tag */)
+                view.removeOnLayoutChangeListener(listener)
+            }
+
+            if (view is ViewGroup) {
+                for (i in 0 until view.childCount) {
+                    recursivelyRemoveListener(view.getChildAt(i))
+                }
+            }
+        }
+
         private fun getBound(view: View, bound: Bound): Int? {
             return view.getTag(bound.overrideTag) as? Int
         }
@@ -513,11 +517,10 @@
                     // When an animation is cancelled, a new one might be taking over. We shouldn't
                     // unregister the listener yet.
                     if (ephemeral && !cancelled) {
-                        val listener = view.getTag(R.id.tag_layout_listener)
-                        if (listener != null && listener is View.OnLayoutChangeListener) {
-                            view.setTag(R.id.tag_layout_listener, null /* tag */)
-                            view.removeOnLayoutChangeListener(listener)
-                        }
+                        // The duration is the same for the whole hierarchy, so it's safe to remove
+                        // the listener recursively. We do this because some descendant views might
+                        // not change bounds, and therefore not animate and leak the listener.
+                        recursivelyRemoveListener(view)
                     }
                 }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 120b09a..de35514 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -41,6 +41,7 @@
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.IRecentsAnimationController;
 import android.view.SurfaceControl;
 import android.window.IRemoteTransition;
@@ -244,22 +245,28 @@
         @SuppressLint("NewApi")
         boolean merge(TransitionInfo info, SurfaceControl.Transaction t,
                 RecentsAnimationListener recents) {
-            ArrayList<TransitionInfo.Change> openingTasks = null;
+            SparseArray<TransitionInfo.Change> openingTasks = null;
             boolean cancelRecents = false;
             boolean homeGoingAway = false;
             boolean hasChangingApp = false;
             for (int i = info.getChanges().size() - 1; i >= 0; --i) {
                 final TransitionInfo.Change change = info.getChanges().get(i);
                 if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
-                    if (change.getTaskInfo() != null) {
-                        if (change.getTaskInfo().topActivityType == ACTIVITY_TYPE_HOME) {
+                    final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+                    if (taskInfo != null) {
+                        if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
                             // canceling recents animation
                             cancelRecents = true;
                         }
                         if (openingTasks == null) {
-                            openingTasks = new ArrayList<>();
+                            openingTasks = new SparseArray<>();
                         }
-                        openingTasks.add(change);
+                        if (taskInfo.hasParentTask()) {
+                            // Collects opening leaf tasks only since Launcher monitors leaf task
+                            // ids to perform recents animation.
+                            openingTasks.remove(taskInfo.parentTaskId);
+                        }
+                        openingTasks.put(taskInfo.taskId, change);
                     }
                 } else if (change.getMode() == TRANSIT_CLOSE
                         || change.getMode() == TRANSIT_TO_BACK) {
@@ -287,7 +294,7 @@
             int pauseMatches = 0;
             if (!cancelRecents) {
                 for (int i = 0; i < openingTasks.size(); ++i) {
-                    if (mPausingTasks.contains(openingTasks.get(i).getContainer())) {
+                    if (mPausingTasks.contains(openingTasks.valueAt(i).getContainer())) {
                         ++pauseMatches;
                     }
                 }
@@ -308,10 +315,11 @@
             final RemoteAnimationTargetCompat[] targets =
                     new RemoteAnimationTargetCompat[openingTasks.size()];
             for (int i = 0; i < openingTasks.size(); ++i) {
-                mOpeningLeashes.add(openingTasks.get(i).getLeash());
+                final TransitionInfo.Change change = openingTasks.valueAt(i);
+                mOpeningLeashes.add(change.getLeash());
                 // We are receiving new opening tasks, so convert to onTasksAppeared.
                 final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat(
-                        openingTasks.get(i), layer, info, t);
+                        change, layer, info, t);
                 mLeashMap.put(mOpeningLeashes.get(i), target.leash);
                 t.reparent(target.leash, mInfo.getRootLeash());
                 t.setLayer(target.leash, layer);
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt
index ad8c126..19d39d5 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt
@@ -22,6 +22,7 @@
 import android.annotation.SuppressLint
 import android.content.Context
 import android.graphics.Canvas
+import android.text.TextUtils
 import android.text.format.DateFormat
 import android.util.AttributeSet
 import android.util.Log
@@ -125,13 +126,30 @@
 
     fun refreshTime() {
         time.timeInMillis = System.currentTimeMillis()
-        text = DateFormat.format(format, time)
         contentDescription = DateFormat.format(descFormat, time)
-        Log.d(tag, "refreshTime this=$this" +
-                " currTimeContextDesc=$contentDescription" +
-                " measuredHeight=$measuredHeight" +
-                " lastMeasureCall=$lastMeasureCall" +
-                " isSingleLineInternal=$isSingleLineInternal")
+        val formattedText = DateFormat.format(format, time)
+        // Setting text actually triggers a layout pass (because the text view is set to
+        // wrap_content width and TextView always relayouts for this). Avoid needless
+        // relayout if the text didn't actually change.
+        if (!TextUtils.equals(text, formattedText)) {
+            text = formattedText
+            Log.d(
+                tag, "refreshTime this=$this" +
+                        " currTimeContextDesc=$contentDescription" +
+                        " measuredHeight=$measuredHeight" +
+                        " lastMeasureCall=$lastMeasureCall" +
+                        " isSingleLineInternal=$isSingleLineInternal"
+            )
+        } else {
+            Log.d(
+                tag, "refreshTime (skipped due to unchanged text)" +
+                        " this=$this" +
+                        " currTimeContextDesc=$contentDescription" +
+                        " measuredHeight=$measuredHeight" +
+                        " lastMeasureCall=$lastMeasureCall" +
+                        " isSingleLineInternal=$isSingleLineInternal"
+            )
+        }
     }
 
     fun onTimeZoneChanged(timeZone: TimeZone?) {
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
index 2b0c083..f444b37 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -32,6 +32,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.Gravity;
@@ -267,8 +268,15 @@
         if (mBatteryPercentView == null) {
             return;
         }
-        mBatteryPercentView.setText(
-                NumberFormat.getPercentInstance().format(mLevel / 100f));
+
+        String percentText = NumberFormat.getPercentInstance().format(mLevel / 100f);
+        // Setting text actually triggers a layout pass (because the text view is set to
+        // wrap_content width and TextView always relayouts for this). Avoid needless
+        // relayout if the text didn't actually change.
+        if (!TextUtils.equals(mBatteryPercentView.getText(), percentText)) {
+            mBatteryPercentView.setText(percentText);
+        }
+
         setContentDescription(
                 getContext().getString(mCharging ? R.string.accessibility_battery_level_charging
                         : R.string.accessibility_battery_level, mLevel));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 63b2b20..2ac2408 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -376,7 +376,6 @@
                 boolean withinSensorArea =
                         isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView);
                 if (withinSensorArea) {
-                    mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
                     Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0);
                     Log.v(TAG, "onTouch | action down");
                     // The pointer that causes ACTION_DOWN is always at index 0.
@@ -792,6 +791,7 @@
                     + " current: " + mOverlay.getRequestId());
             return;
         }
+        mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
 
         if (!mOnFingerDown) {
             playStartHaptic();
@@ -806,11 +806,9 @@
 
         final UdfpsView view = mOverlay.getOverlayView();
         if (view != null) {
-            Trace.beginAsyncSection("UdfpsController.e2e.startIllumination", 0);
             view.startIllumination(() -> {
                 mFingerprintManager.onUiReady(requestId, mSensorProps.sensorId);
                 mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
-                Trace.endAsyncSection("UdfpsController.e2e.startIllumination", 0);
             });
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
index f710d01..0d89879 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
@@ -95,7 +95,7 @@
     private void share() {
         Intent sendIntent = new Intent();
         sendIntent.setAction(Intent.ACTION_SEND);
-        sendIntent.putExtra(Intent.EXTRA_TEXT, mEditText.getText());
+        sendIntent.putExtra(Intent.EXTRA_TEXT, mEditText.getText().toString());
         sendIntent.setType("text/plain");
 
         Intent shareIntent = Intent.createChooser(sendIntent, null);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index c9a61a8..44580aa 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -162,6 +162,17 @@
     public static final SysPropBooleanFlag WM_ENABLE_SHELL_TRANSITIONS =
             new SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false);
 
+    // 1200 - predictive back
+    @Keep
+    public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag(
+            1200, "persist.wm.debug.predictive_back", true);
+    @Keep
+    public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK_ANIM = new SysPropBooleanFlag(
+            1201, "persist.wm.debug.predictive_back_anim", false);
+    @Keep
+    public static final SysPropBooleanFlag WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
+            new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false);
+
     // Pay no attention to the reflection behind the curtain.
     // ========================== Curtain ==========================
     // |                                                           |
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index 3c6805b..cd86fff 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -19,6 +19,7 @@
 import android.app.StatusBarManager
 import android.content.Context
 import android.media.MediaRoute2Info
+import android.util.Log
 import android.view.View
 import androidx.annotation.StringRes
 import com.android.internal.logging.UiEventLogger
@@ -221,7 +222,12 @@
          */
         fun getSenderStateFromId(
             @StatusBarManager.MediaTransferSenderState displayState: Int,
-        ): ChipStateSender = values().first { it.stateInt == displayState }
+        ): ChipStateSender? = try {
+            values().first { it.stateInt == displayState }
+        } catch (e: NoSuchElementException) {
+            Log.e(TAG, "Could not find requested state $displayState", e)
+            null
+        }
 
         /**
          * Returns the state int from [StatusBarManager] associated with the given sender state
@@ -238,3 +244,5 @@
 // process and we should keep the user informed about it as long as possible (but don't allow it to
 // continue indefinitely).
 private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 15000L
+
+private const val TAG = "ChipStateSender"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index d785059..27586b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -60,6 +60,7 @@
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.SecureSettings;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -84,6 +85,7 @@
 
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final KeyguardStateController mKeyguardStateController;
+    private final SecureSettings mSecureSettings;
     private final Object mLock = new Object();
 
     // Lazy
@@ -187,6 +189,7 @@
     protected NotificationPresenter mPresenter;
     protected ContentObserver mLockscreenSettingsObserver;
     protected ContentObserver mSettingsObserver;
+    private boolean mHideSilentNotificationsOnLockscreen;
 
     private NotificationEntryManager getEntryManager() {
         if (mEntryManager == null) {
@@ -208,6 +211,7 @@
             @Main Handler mainHandler,
             DeviceProvisionedController deviceProvisionedController,
             KeyguardStateController keyguardStateController,
+            SecureSettings secureSettings,
             DumpManager dumpManager) {
         mContext = context;
         mMainHandler = mainHandler;
@@ -222,6 +226,7 @@
         mKeyguardManager = keyguardManager;
         mBroadcastDispatcher = broadcastDispatcher;
         mDeviceProvisionedController = deviceProvisionedController;
+        mSecureSettings = secureSettings;
         mKeyguardStateController = keyguardStateController;
 
         dumpManager.registerDumpable(this);
@@ -256,12 +261,18 @@
         };
 
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
+                mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
                 mLockscreenSettingsObserver,
                 UserHandle.USER_ALL);
 
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+                mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+                true,
+                mLockscreenSettingsObserver,
+                UserHandle.USER_ALL);
+
+        mContext.getContentResolver().registerContentObserver(
+                mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS),
                 true,
                 mLockscreenSettingsObserver,
                 UserHandle.USER_ALL);
@@ -272,7 +283,7 @@
 
         if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
             mContext.getContentResolver().registerContentObserver(
-                    Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
+                    mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
                     false,
                     mSettingsObserver,
                     UserHandle.USER_ALL);
@@ -366,7 +377,7 @@
             }
         }
         boolean exceedsPriorityThreshold;
-        if (hideSilentNotificationsOnLockscreen()) {
+        if (mHideSilentNotificationsOnLockscreen) {
             exceedsPriorityThreshold =
                     entry.getBucket() == BUCKET_MEDIA_CONTROLS
                             || (entry.getBucket() != BUCKET_SILENT
@@ -377,11 +388,6 @@
         return mShowLockscreenNotifications && exceedsPriorityThreshold;
     }
 
-    private boolean hideSilentNotificationsOnLockscreen() {
-        return whitelistIpcs(() -> Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1) == 0);
-    }
-
     private void setShowLockscreenNotifications(boolean show) {
         mShowLockscreenNotifications = show;
     }
@@ -391,7 +397,7 @@
     }
 
     protected void updateLockscreenNotificationSetting() {
-        final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+        final boolean show = mSecureSettings.getIntForUser(
                 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
                 1,
                 mCurrentUserId) != 0;
@@ -400,10 +406,13 @@
         final boolean allowedByDpm = (dpmFlags
                 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
 
+        mHideSilentNotificationsOnLockscreen = mSecureSettings.getIntForUser(
+                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1, mCurrentUserId) == 0;
+
         setShowLockscreenNotifications(show && allowedByDpm);
 
         if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
-            final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+            final boolean remoteInput = mSecureSettings.getIntForUser(
                     Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
                     0,
                     mCurrentUserId) != 0;
@@ -426,8 +435,7 @@
         }
 
         if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
-            final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
-                    mContext.getContentResolver(),
+            final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
             final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
                     DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
@@ -492,8 +500,7 @@
         }
 
         if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
-            final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
-                    mContext.getContentResolver(),
+            final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
                     Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
             final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
                     DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index c640ab6..7d0f00a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -270,8 +270,8 @@
 
     /** Set the current expand animation size. */
     public void setExpandAnimationSize(int width, int height) {
-        mExpandAnimationHeight = width;
-        mExpandAnimationWidth = height;
+        mExpandAnimationHeight = height;
+        mExpandAnimationWidth = width;
         invalidate();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 6007323..6887741 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -405,7 +405,7 @@
      * Determines if QS should be already expanded when expanding shade.
      * Used for split shade, two finger gesture as well as accessibility shortcut to QS.
      */
-    private boolean mQsExpandImmediate;
+    @VisibleForTesting boolean mQsExpandImmediate;
     private boolean mTwoFingerQsExpandPossible;
     private String mHeaderDebugInfo;
 
@@ -430,7 +430,6 @@
     private boolean mExpandingFromHeadsUp;
     private boolean mCollapsedOnDown;
     private int mPositionMinSideMargin;
-    private int mLastOrientation = -1;
     private boolean mClosingWithAlphaFadeOut;
     private boolean mHeadsUpAnimatingAway;
     private boolean mLaunchingAffordance;
@@ -955,7 +954,6 @@
         mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area);
         mPreviewContainer = mView.findViewById(R.id.preview_container);
         mKeyguardBottomArea.setPreviewContainer(mPreviewContainer);
-        mLastOrientation = mResources.getConfiguration().orientation;
 
         initBottomArea();
 
@@ -1994,9 +1992,6 @@
         if (!isFullyCollapsed()) {
             return;
         }
-        if (mShouldUseSplitNotificationShade) {
-            mQsExpandImmediate = true;
-        }
         mExpectingSynthesizedDown = true;
         onTrackingStarted();
         updatePanelExpanded();
@@ -3172,6 +3167,9 @@
         mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
         super.onTrackingStarted();
         mScrimController.onTrackingStarted();
+        if (mShouldUseSplitNotificationShade) {
+            mQsExpandImmediate = true;
+        }
         if (mQsFullyExpanded) {
             mQsExpandImmediate = true;
             setShowShelfOnly(true);
@@ -4876,7 +4874,6 @@
         public void onConfigurationChanged(Configuration newConfig) {
             super.onConfigurationChanged(newConfig);
             mAffordanceHelper.onConfigurationChanged();
-            mLastOrientation = newConfig.orientation;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 9e1e87b..4c43734 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -31,6 +31,7 @@
 import android.os.UserHandle;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
 import android.text.format.DateFormat;
 import android.text.style.CharacterStyle;
 import android.text.style.RelativeSizeSpan;
@@ -291,7 +292,13 @@
     final void updateClock() {
         if (mDemoMode) return;
         mCalendar.setTimeInMillis(System.currentTimeMillis());
-        setText(getSmallTime());
+        CharSequence smallTime = getSmallTime();
+        // Setting text actually triggers a layout pass (because the text view is set to
+        // wrap_content width and TextView always relayouts for this). Avoid needless
+        // relayout if the text didn't actually change.
+        if (!TextUtils.equals(smallTime, getText())) {
+            setText(smallTime);
+        }
         setContentDescription(mContentDescriptionFormat.format(mCalendar.getTime()));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
index 98d57a3..1c84ee9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
@@ -522,6 +522,38 @@
     }
 
     @Test
+    fun cleansUpListenersCorrectly() {
+        val firstChild = View(mContext)
+        firstChild.layoutParams = LinearLayout.LayoutParams(50 /* width */, 100 /* height */)
+        rootView.addView(firstChild)
+        val secondChild = View(mContext)
+        secondChild.layoutParams = LinearLayout.LayoutParams(50 /* width */, 100 /* height */)
+        rootView.addView(secondChild)
+        rootView.measure(
+            View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY),
+            View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
+        )
+        rootView.layout(0 /* l */, 0 /* t */, 100 /* r */, 100 /* b */)
+
+        val success = ViewHierarchyAnimator.animateNextUpdate(rootView)
+        // Change all bounds.
+        rootView.measure(
+            View.MeasureSpec.makeMeasureSpec(150, View.MeasureSpec.EXACTLY),
+            View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
+        )
+        rootView.layout(0 /* l */, 0 /* t */, 150 /* r */, 100 /* b */)
+
+        assertTrue(success)
+        assertNotNull(rootView.getTag(R.id.tag_layout_listener))
+        assertNotNull(firstChild.getTag(R.id.tag_layout_listener))
+        assertNotNull(secondChild.getTag(R.id.tag_layout_listener))
+        endAnimation(rootView)
+        assertNull(rootView.getTag(R.id.tag_layout_listener))
+        assertNull(firstChild.getTag(R.id.tag_layout_listener))
+        assertNull(secondChild.getTag(R.id.tag_layout_listener))
+    }
+
+    @Test
     fun doesNotAnimateInvisibleViews() {
         rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index ef53154..9a01464 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -277,6 +277,17 @@
     }
 
     @Test
+    fun commandQueueCallback_invalidStateParam_noChipShown() {
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            100,
+            routeInfo,
+            null
+        )
+
+        verify(windowManager, never()).addView(any(), any())
+    }
+
+    @Test
     fun receivesNewStateFromCommandQueue_isLogged() {
         commandQueueCallback.updateMediaTapToTransferSenderDisplay(
             StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 48f8206..7687d12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -66,6 +66,7 @@
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.FakeSettings;
 
 import com.google.android.collect.Lists;
 
@@ -109,6 +110,7 @@
     private UserInfo mCurrentUser;
     private UserInfo mSecondaryUser;
     private UserInfo mWorkUser;
+    private FakeSettings mSettings;
     private TestNotificationLockscreenUserManager mLockscreenUserManager;
     private NotificationEntry mCurrentUserNotif;
     private NotificationEntry mSecondaryUserNotif;
@@ -120,6 +122,8 @@
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
 
         int currentUserId = ActivityManager.getCurrentUser();
+        mSettings = new FakeSettings();
+        mSettings.setUserId(ActivityManager.getCurrentUser());
         mCurrentUser = new UserInfo(currentUserId, "", 0);
         mSecondaryUser = new UserInfo(currentUserId + 1, "", 0);
         mWorkUser = new UserInfo(currentUserId + 2, "" /* name */, null /* iconPath */, 0,
@@ -157,48 +161,45 @@
 
     @Test
     public void testLockScreenShowNotificationsFalse() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications());
     }
 
     @Test
     public void testLockScreenShowNotificationsTrue() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications());
     }
 
     @Test
     public void testLockScreenAllowPrivateNotificationsTrue() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
     }
 
     @Test
     public void testLockScreenAllowPrivateNotificationsFalse() {
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mCurrentUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
     }
 
     @Test
     public void testLockScreenAllowsWorkPrivateNotificationsFalse() {
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mWorkUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
     }
 
     @Test
     public void testLockScreenAllowsWorkPrivateNotificationsTrue() {
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mWorkUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
     }
@@ -206,8 +207,8 @@
     @Test
     public void testCurrentUserPrivateNotificationsNotRedacted() {
         // GIVEN current user doesn't allow private notifications to show
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mCurrentUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         // THEN current user's notification is redacted
@@ -217,8 +218,8 @@
     @Test
     public void testCurrentUserPrivateNotificationsRedacted() {
         // GIVEN current user allows private notifications to show
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mCurrentUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         // THEN current user's notification isn't redacted
@@ -228,8 +229,8 @@
     @Test
     public void testWorkPrivateNotificationsRedacted() {
         // GIVEN work profile doesn't private notifications to show
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mWorkUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         // THEN work profile notification is redacted
@@ -239,8 +240,8 @@
     @Test
     public void testWorkPrivateNotificationsNotRedacted() {
         // GIVEN work profile allows private notifications to show
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mWorkUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         // THEN work profile notification isn't redacted
@@ -250,12 +251,11 @@
     @Test
     public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() {
         // GIVEN work profile allows private notifications to show but the other users don't
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id);
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mWorkUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mCurrentUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
                 mSecondaryUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
@@ -270,12 +270,11 @@
     @Test
     public void testWorkProfileRedacted_otherUsersNotRedacted() {
         // GIVEN work profile doesn't allow private notifications to show but the other users do
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id);
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id);
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mWorkUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mCurrentUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
                 mSecondaryUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
@@ -291,10 +290,9 @@
     public void testSecondaryUserNotRedacted_currentUserRedacted() {
         // GIVEN secondary profile allows private notifications to show but the current user
         // doesn't allow private notifications to show
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mCurrentUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
                 mSecondaryUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
@@ -328,10 +326,9 @@
 
     @Test
     public void testShowSilentNotifications_settingSaysShow() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         NotificationEntry entry = new NotificationEntryBuilder()
                 .setImportance(IMPORTANCE_LOW)
@@ -343,10 +340,9 @@
 
     @Test
     public void testShowSilentNotifications_settingSaysHide() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         final Notification notification = mock(Notification.class);
         when(notification.isForegroundService()).thenReturn(true);
@@ -360,10 +356,9 @@
 
     @Test
     public void testShowSilentNotificationsPeopleBucket_settingSaysHide() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         final Notification notification = mock(Notification.class);
         when(notification.isForegroundService()).thenReturn(true);
@@ -377,10 +372,9 @@
 
     @Test
     public void testShowSilentNotificationsMediaBucket_settingSaysHide() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         final Notification notification = mock(Notification.class);
         when(notification.isForegroundService()).thenReturn(true);
@@ -396,8 +390,8 @@
     @Test
     public void testKeyguardNotificationSuppressors() {
         // GIVEN a notification that should be shown on the lockscreen
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         final NotificationEntry entry = new NotificationEntryBuilder()
                 .setImportance(IMPORTANCE_HIGH)
                 .build();
@@ -433,6 +427,7 @@
                     Handler.createAsync(Looper.myLooper()),
                     mDeviceProvisionedController,
                     mKeyguardStateController,
+                    mSettings,
                     mock(DumpManager.class));
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 7ef656c..8f3df09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -947,6 +947,15 @@
         verify(mKeyguardStatusBarViewController).setAlpha(statusBarAlpha);
     }
 
+    @Test
+    public void testQsToBeImmediatelyExpandedInSplitShade() {
+        enableSplitShade(/* enabled= */ true);
+
+        mNotificationPanelViewController.onTrackingStarted();
+
+        assertThat(mNotificationPanelViewController.mQsExpandImmediate).isTrue();
+    }
+
     private void triggerPositionClockAndNotifications() {
         mNotificationPanelViewController.closeQs();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
index e66491e..e660e1f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.util.settings;
 
+import android.annotation.UserIdInt;
 import android.content.ContentResolver;
 import android.database.ContentObserver;
 import android.net.Uri;
@@ -34,6 +35,8 @@
     private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>();
 
     public static final Uri CONTENT_URI = Uri.parse("content://settings/fake");
+    @UserIdInt
+    private int mUserId = UserHandle.USER_CURRENT;
 
     public FakeSettings() {
     }
@@ -85,9 +88,13 @@
         return Uri.withAppendedPath(CONTENT_URI, name);
     }
 
+    public void setUserId(@UserIdInt int userId) {
+        mUserId = userId;
+    }
+
     @Override
     public int getUserId() {
-        return UserHandle.USER_CURRENT;
+        return mUserId;
     }
 
     @Override
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 62ba0c8..b263fb3 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -40,6 +40,8 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+import android.util.SparseLongArray;
 import android.view.accessibility.MagnificationAnimationCallback;
 
 import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
@@ -97,20 +99,22 @@
     private final boolean mSupportWindowMagnification;
 
     @GuardedBy("mLock")
-    private int mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+    private final SparseIntArray mCurrentMagnificationModeArray = new SparseIntArray();
     @GuardedBy("mLock")
-    private int mLastActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+    private final SparseIntArray mLastMagnificationActivatedModeArray = new SparseIntArray();
     // Track the active user to reset the magnification and get the associated user settings.
     private @UserIdInt int mUserId = UserHandle.USER_SYSTEM;
     @GuardedBy("mLock")
     private final SparseBooleanArray mIsImeVisibleArray = new SparseBooleanArray();
-    private long mWindowModeEnabledTime = 0;
-    private long mFullScreenModeEnabledTime = 0;
+    @GuardedBy("mLock")
+    private final SparseLongArray mWindowModeEnabledTimeArray = new SparseLongArray();
+    @GuardedBy("mLock")
+    private final SparseLongArray mFullScreenModeEnabledTimeArray = new SparseLongArray();
 
     @GuardedBy("mLock")
-    @Nullable
-    private WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks
-            mAccessibilityCallbacksDelegate;
+    private final SparseArray<WindowManagerInternal.AccessibilityControllerInternal
+            .UiChangesForAccessibilityCallbacks> mAccessibilityCallbacksDelegateArray =
+            new SparseArray<>();
 
     /**
      * A callback to inform the magnification transition result on the given display.
@@ -333,20 +337,20 @@
     }
 
     @GuardedBy("mLock")
-    private void setActivatedModeAndSwitchDelegate(int mode) {
-        mActivatedMode = mode;
-        assignMagnificationWindowManagerDelegateByMode(mode);
+    private void setCurrentMagnificationModeAndSwitchDelegate(int displayId, int mode) {
+        mCurrentMagnificationModeArray.put(displayId, mode);
+        assignMagnificationWindowManagerDelegateByMode(displayId, mode);
     }
 
-    private void assignMagnificationWindowManagerDelegateByMode(int mode) {
-        synchronized (mLock) {
-            if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
-                mAccessibilityCallbacksDelegate = getFullScreenMagnificationController();
-            } else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
-                mAccessibilityCallbacksDelegate = getWindowMagnificationMgr();
-            } else {
-                mAccessibilityCallbacksDelegate = null;
-            }
+    @GuardedBy("mLock")
+    private void assignMagnificationWindowManagerDelegateByMode(int displayId, int mode) {
+        if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
+            mAccessibilityCallbacksDelegateArray.put(displayId,
+                    getFullScreenMagnificationController());
+        } else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
+            mAccessibilityCallbacksDelegateArray.put(displayId, getWindowMagnificationMgr());
+        } else {
+            mAccessibilityCallbacksDelegateArray.delete(displayId);
         }
     }
 
@@ -356,7 +360,7 @@
         WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks
                 delegate;
         synchronized (mLock) {
-            delegate = mAccessibilityCallbacksDelegate;
+            delegate = mAccessibilityCallbacksDelegateArray.get(displayId);
         }
         if (delegate != null) {
             delegate.onRectangleOnScreenRequested(displayId, left, top, right, bottom);
@@ -378,25 +382,26 @@
         }
     }
 
-    // TODO : supporting multi-display (b/182227245).
     @Override
     public void onWindowMagnificationActivationState(int displayId, boolean activated) {
         if (activated) {
-            mWindowModeEnabledTime = SystemClock.uptimeMillis();
-
             synchronized (mLock) {
-                setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
-                mLastActivatedMode = mActivatedMode;
+                mWindowModeEnabledTimeArray.put(displayId, SystemClock.uptimeMillis());
+                setCurrentMagnificationModeAndSwitchDelegate(displayId,
+                        ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+                mLastMagnificationActivatedModeArray.put(displayId,
+                        ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
             }
             logMagnificationModeWithImeOnIfNeeded(displayId);
             disableFullScreenMagnificationIfNeeded(displayId);
         } else {
-            logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
-                    SystemClock.uptimeMillis() - mWindowModeEnabledTime);
-
+            long duration;
             synchronized (mLock) {
-                setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
+                setCurrentMagnificationModeAndSwitchDelegate(displayId,
+                        ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
+                duration = SystemClock.uptimeMillis() - mWindowModeEnabledTimeArray.get(displayId);
             }
+            logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, duration);
         }
         updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
     }
@@ -437,21 +442,24 @@
     @Override
     public void onFullScreenMagnificationActivationState(int displayId, boolean activated) {
         if (activated) {
-            mFullScreenModeEnabledTime = SystemClock.uptimeMillis();
-
             synchronized (mLock) {
-                setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-                mLastActivatedMode = mActivatedMode;
+                mFullScreenModeEnabledTimeArray.put(displayId, SystemClock.uptimeMillis());
+                setCurrentMagnificationModeAndSwitchDelegate(displayId,
+                        ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+                mLastMagnificationActivatedModeArray.put(displayId,
+                        ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
             }
             logMagnificationModeWithImeOnIfNeeded(displayId);
             disableWindowMagnificationIfNeeded(displayId);
         } else {
-            logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
-                    SystemClock.uptimeMillis() - mFullScreenModeEnabledTime);
-
+            long duration;
             synchronized (mLock) {
-                setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
+                setCurrentMagnificationModeAndSwitchDelegate(displayId,
+                        ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
+                duration = SystemClock.uptimeMillis()
+                        - mFullScreenModeEnabledTimeArray.get(displayId);
             }
+            logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, duration);
         }
         updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
     }
@@ -477,9 +485,10 @@
      * Returns the last activated magnification mode. If there is no activated magnifier before, it
      * returns fullscreen mode by default.
      */
-    public int getLastActivatedMode() {
+    public int getLastMagnificationActivatedMode(int displayId) {
         synchronized (mLock) {
-            return mLastActivatedMode;
+            return mLastMagnificationActivatedModeArray.get(displayId,
+                    ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
         }
     }
 
@@ -522,7 +531,10 @@
         synchronized (mLock) {
             fullMagnificationController = mFullScreenMagnificationController;
             windowMagnificationManager = mWindowMagnificationMgr;
-            mLastActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+            mAccessibilityCallbacksDelegateArray.clear();
+            mCurrentMagnificationModeArray.clear();
+            mLastMagnificationActivatedModeArray.clear();
+            mIsImeVisibleArray.clear();
         }
 
         mScaleProvider.onUserChanged(userId);
@@ -547,6 +559,10 @@
             if (mWindowMagnificationMgr != null) {
                 mWindowMagnificationMgr.onDisplayRemoved(displayId);
             }
+            mAccessibilityCallbacksDelegateArray.delete(displayId);
+            mCurrentMagnificationModeArray.delete(displayId);
+            mLastMagnificationActivatedModeArray.delete(displayId);
+            mIsImeVisibleArray.delete(displayId);
         }
         mScaleProvider.onDisplayRemoved(displayId);
     }
@@ -587,16 +603,17 @@
     }
 
     private void logMagnificationModeWithImeOnIfNeeded(int displayId) {
-        final int mode;
+        final int currentActivateMode;
 
         synchronized (mLock) {
+            currentActivateMode = mCurrentMagnificationModeArray.get(displayId,
+                    ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
             if (!mIsImeVisibleArray.get(displayId, false)
-                    || mActivatedMode == ACCESSIBILITY_MAGNIFICATION_MODE_NONE) {
+                    || currentActivateMode == ACCESSIBILITY_MAGNIFICATION_MODE_NONE) {
                 return;
             }
-            mode = mActivatedMode;
         }
-        logMagnificationModeWithIme(mode);
+        logMagnificationModeWithIme(currentActivateMode);
     }
 
     /**
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index 3e07b09..a356ae6 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -341,7 +341,8 @@
                 ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)) {
             return MAGNIFICATION_MODE_FULLSCREEN;
         } else {
-            return (mController.getLastActivatedMode() == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)
+            return (mController.getLastMagnificationActivatedMode(displayId)
+                    == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)
                     ? MAGNIFICATION_MODE_WINDOW
                     : MAGNIFICATION_MODE_FULLSCREEN;
         }
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index f34c506..06f698e 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1208,6 +1208,11 @@
     public abstract SharedUserApi getSharedUserApi(int sharedUserAppId);
 
     /**
+     * Returns if the given uid is privileged or not.
+     */
+    public abstract boolean isUidPrivileged(int uid);
+
+    /**
      * Initiates a package state mutation request, returning the current state as known by
      * PackageManager. This allows the later commit request to compare the initial values and
      * determine if any state was changed or any packages were updated since the whole request
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index e6b7a4c..b059cc7 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2977,19 +2977,21 @@
                  * outside of those expected to be injected by the AccountManager, e.g.
                  * ANDORID_PACKAGE_NAME.
                  */
-                String token = readCachedTokenInternal(
+                TokenCache.Value cachedToken = readCachedTokenInternal(
                         accounts,
                         account,
                         authTokenType,
                         callerPkg,
                         callerPkgSigDigest);
-                if (token != null) {
+                if (cachedToken != null) {
                     logGetAuthTokenMetrics(callerPkg, account.type);
                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
                         Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
                     }
                     Bundle result = new Bundle();
-                    result.putString(AccountManager.KEY_AUTHTOKEN, token);
+                    result.putString(AccountManager.KEY_AUTHTOKEN, cachedToken.token);
+                    result.putLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY,
+                            cachedToken.expiryEpochMillis);
                     result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
                     result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
                     onResult(response, result);
@@ -6121,7 +6123,7 @@
         }
     }
 
-    protected String readCachedTokenInternal(
+    protected TokenCache.Value readCachedTokenInternal(
             UserAccounts accounts,
             Account account,
             String tokenType,
diff --git a/services/core/java/com/android/server/accounts/TokenCache.java b/services/core/java/com/android/server/accounts/TokenCache.java
index 66e550f..9427ee4 100644
--- a/services/core/java/com/android/server/accounts/TokenCache.java
+++ b/services/core/java/com/android/server/accounts/TokenCache.java
@@ -20,8 +20,6 @@
 import android.util.LruCache;
 import android.util.Pair;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -35,7 +33,8 @@
 
     private static final int MAX_CACHE_CHARS = 64000;
 
-    private static class Value {
+    /** Package private*/
+    static class Value {
         public final String token;
         public final long expiryEpochMillis;
 
@@ -217,12 +216,12 @@
     /**
      * Gets a token from the cache if possible.
      */
-    public String get(Account account, String tokenType, String packageName, byte[] sigDigest) {
+    public Value get(Account account, String tokenType, String packageName, byte[] sigDigest) {
         Key k = new Key(account, tokenType, packageName, sigDigest);
         Value v = mCachedTokens.get(k);
         long currentTime = System.currentTimeMillis();
         if (v != null && currentTime < v.expiryEpochMillis) {
-            return v.token;
+            return v;
         } else if (v != null) {
             remove(account.type, v.token);
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index ccb27ee..576a5ff 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -396,7 +396,9 @@
         try {
             String iso3Language = new String(message.getParams(), 0, 3, "US-ASCII");
             Locale currentLocale = mService.getContext().getResources().getConfiguration().locale;
-            if (currentLocale.getISO3Language().equals(iso3Language)) {
+            String curIso3Language = mService.localeToMenuLanguage(currentLocale);
+            HdmiLogger.debug("handleSetMenuLanguage " + iso3Language + " cur:" + curIso3Language);
+            if (curIso3Language.equals(iso3Language)) {
                 // Do not switch language if the new language is the same as the current one.
                 // This helps avoid accidental country variant switching from en_US to en_AU
                 // due to the limitation of CEC. See the warning below.
@@ -408,7 +410,7 @@
             final List<LocaleInfo> localeInfos = LocalePicker.getAllAssetLocales(
                     mService.getContext(), false);
             for (LocaleInfo localeInfo : localeInfos) {
-                if (localeInfo.getLocale().getISO3Language().equals(iso3Language)) {
+                if (mService.localeToMenuLanguage(localeInfo.getLocale()).equals(iso3Language)) {
                     // WARNING: CEC adopts ISO/FDIS-2 for language code, while Android requires
                     // additional country variant to pinpoint the locale. This keeps the right
                     // locale from being chosen. 'eng' in the CEC command, for instance,
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index a73c8e0..0e4bbbb 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -18,12 +18,14 @@
 
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 
 import android.app.ActivityManager;
 import android.app.admin.PasswordMetrics;
 import android.content.Context;
 import android.os.ShellCommand;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Slog;
 
@@ -48,6 +50,8 @@
     private static final String COMMAND_REMOVE_CACHE = "remove-cache";
     private static final String COMMAND_SET_ROR_PROVIDER_PACKAGE =
             "set-resume-on-reboot-provider-package";
+    private static final String COMMAND_REQUIRE_STRONG_AUTH =
+            "require-strong-auth";
     private static final String COMMAND_HELP = "help";
 
     private int mCurrentUserId;
@@ -97,6 +101,9 @@
                 case COMMAND_SET_ROR_PROVIDER_PACKAGE:
                     runSetResumeOnRebootProviderPackage();
                     return 0;
+                case COMMAND_REQUIRE_STRONG_AUTH:
+                    runRequireStrongAuth();
+                    return 0;
                 case COMMAND_HELP:
                     onHelp();
                     return 0;
@@ -192,6 +199,10 @@
             pw.println("    Sets the package name for server based resume on reboot service "
                     + "provider.");
             pw.println("");
+            pw.println("  require-strong-auth [--user USER_ID] <reason>");
+            pw.println("    Requires the strong authentication. The current supported reasons: "
+                    + "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN.");
+            pw.println("");
         }
     }
 
@@ -288,6 +299,24 @@
         return true;
     }
 
+    private boolean runRequireStrongAuth() {
+        final String reason = mNew;
+        int strongAuthReason;
+        switch (reason) {
+            case "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN":
+                strongAuthReason = STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+                mCurrentUserId = UserHandle.USER_ALL;
+                break;
+            default:
+                getErrPrintWriter().println("Unsupported reason: " + reason);
+                return false;
+        }
+        mLockPatternUtils.requireStrongAuth(strongAuthReason, mCurrentUserId);
+        getOutPrintWriter().println("Require strong auth for USER_ID "
+                + mCurrentUserId + " because of " + mNew);
+        return true;
+    }
+
     private boolean runClear() {
         LockscreenCredential none = LockscreenCredential.createNone();
         if (!isNewCredentialSufficient(none)) {
diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
index 8be90e0c..b45bfb1 100644
--- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
+++ b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
@@ -30,6 +30,7 @@
 import android.os.UserHandle;
 import android.os.logcat.ILogcatManagerService;
 import android.util.Slog;
+import android.view.InflateException;
 import android.view.View;
 import android.widget.Button;
 import android.widget.TextView;
@@ -56,33 +57,46 @@
     private String mAlertTitle;
     private AlertDialog.Builder mAlertDialog;
     private AlertDialog mAlert;
+    private View mAlertView;
 
     private static final int DIALOG_TIME_OUT = Build.IS_DEBUGGABLE ? 60000 : 300000;
     private static final int MSG_DISMISS_DIALOG = 0;
 
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mContext = this;
 
-        Intent intent = getIntent();
-        mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
-        mUid = intent.getIntExtra("com.android.server.logcat.uid", 0);
-        mGid = intent.getIntExtra("com.android.server.logcat.gid", 0);
-        mPid = intent.getIntExtra("com.android.server.logcat.pid", 0);
-        mFd = intent.getIntExtra("com.android.server.logcat.fd", 0);
-        mAlertTitle = getTitleString(mContext, mPackageName, mUid);
+        try {
+            mContext = this;
 
-        if (mAlertTitle != null) {
+            // retrieve Intent extra information
+            Intent intent = getIntent();
+            getIntentInfo(intent);
 
+            // retrieve the title string from passed intent extra
+            mAlertTitle = getTitleString(mContext, mPackageName, mUid);
+
+            // creaet View
+            mAlertView = createView();
+
+            // create AlertDialog
             mAlertDialog = new AlertDialog.Builder(this);
-            mAlertDialog.setView(createView());
+            mAlertDialog.setView(mAlertView);
 
+            // show Alert
             mAlert = mAlertDialog.create();
             mAlert.show();
+
+            // set Alert Timeout
             mHandler.sendEmptyMessageDelayed(MSG_DISMISS_DIALOG, DIALOG_TIME_OUT);
 
+        } catch (Exception e) {
+            try {
+                Slog.e(TAG, "onCreate failed, declining the logd access", e);
+                mLogcatManagerService.decline(mUid, mGid, mPid, mFd);
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "Fails to call remote functions", ex);
+            }
         }
     }
 
@@ -95,6 +109,19 @@
         mAlert = null;
     }
 
+    private void getIntentInfo(Intent intent) throws Exception {
+
+        if (intent == null) {
+            throw new NullPointerException("Intent is null");
+        }
+
+        mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+        mUid = intent.getIntExtra("com.android.server.logcat.uid", 0);
+        mGid = intent.getIntExtra("com.android.server.logcat.gid", 0);
+        mPid = intent.getIntExtra("com.android.server.logcat.pid", 0);
+        mFd = intent.getIntExtra("com.android.server.logcat.fd", 0);
+    }
+
     private Handler mHandler = new Handler() {
         public void handleMessage(android.os.Message msg) {
             switch (msg.what) {
@@ -116,26 +143,41 @@
         }
     };
 
-    private String getTitleString(Context context, String callingPackage, int uid) {
+    private String getTitleString(Context context, String callingPackage, int uid)
+            throws Exception {
+
         PackageManager pm = context.getPackageManager();
-        try {
-            return context.getString(
-                    com.android.internal.R.string.log_access_confirmation_title,
-                    pm.getApplicationInfoAsUser(callingPackage,
-                            PackageManager.MATCH_DIRECT_BOOT_AUTO,
-                            UserHandle.getUserId(uid)).loadLabel(pm));
-        } catch (NameNotFoundException e) {
-            Slog.e(TAG, "App name is unknown.", e);
-            return null;
+        if (pm == null) {
+            throw new NullPointerException("PackageManager is null");
         }
+
+        CharSequence appLabel = pm.getApplicationInfoAsUser(callingPackage,
+                PackageManager.MATCH_DIRECT_BOOT_AUTO,
+                UserHandle.getUserId(uid)).loadLabel(pm);
+        if (appLabel == null) {
+            throw new NameNotFoundException("Application Label is null");
+        }
+
+        return context.getString(com.android.internal.R.string.log_access_confirmation_title,
+            appLabel);
     }
 
-    private View createView() {
+    /**
+     * Returns the dialog view.
+     * If we cannot retrieve the package name, it returns null and we decline the full device log
+     * access
+     */
+    private View createView() throws Exception {
+
         final View view = getLayoutInflater().inflate(
                 R.layout.log_access_user_consent_dialog_permission, null /*root*/);
 
+        if (view == null) {
+            throw new InflateException();
+        }
+
         ((TextView) view.findViewById(R.id.log_access_dialog_title))
-                .setText(mAlertTitle);
+            .setText(mAlertTitle);
 
         Button button_allow = (Button) view.findViewById(R.id.log_access_dialog_allow_button);
         button_allow.setOnClickListener(this);
@@ -144,6 +186,7 @@
         button_deny.setOnClickListener(this);
 
         return view;
+
     }
 
     @Override
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
index 015bf3c5..4c265ad 100644
--- a/services/core/java/com/android/server/logcat/LogcatManagerService.java
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -102,16 +102,27 @@
         }
     }
 
-    private void showDialog(int uid, int gid, int pid, int fd) {
+    /**
+     * Returns the package name.
+     * If we cannot retrieve the package name, it returns null and we decline the full device log
+     * access
+     */
+    private String getPackageName(int uid, int gid, int pid, int fd) {
+
         final ActivityManagerInternal activityManagerInternal =
                 LocalServices.getService(ActivityManagerInternal.class);
+        if (activityManagerInternal != null) {
+            String packageName = activityManagerInternal.getPackageNameByPid(pid);
+            if (packageName != null) {
+                return packageName;
+            }
+        }
 
         PackageManager pm = mContext.getPackageManager();
-        String packageName = activityManagerInternal.getPackageNameByPid(pid);
-        if (packageName != null) {
-            Intent mIntent = createIntent(packageName, uid, gid, pid, fd);
-            mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM);
-            return;
+        if (pm == null) {
+            // Decline the logd access if PackageManager is null
+            Slog.e(TAG, "PackageManager is null, declining the logd access");
+            return null;
         }
 
         String[] packageNames = pm.getPackagesForUid(uid);
@@ -119,21 +130,19 @@
         if (ArrayUtils.isEmpty(packageNames)) {
             // Decline the logd access if the app name is unknown
             Slog.e(TAG, "Unknown calling package name, declining the logd access");
-            declineLogdAccess(uid, gid, pid, fd);
-            return;
+            return null;
         }
 
         String firstPackageName = packageNames[0];
 
-        if (firstPackageName.isEmpty() || firstPackageName == null) {
+        if (firstPackageName == null || firstPackageName.isEmpty()) {
             // Decline the logd access if the package name from uid is unknown
             Slog.e(TAG, "Unknown calling package name, declining the logd access");
-            declineLogdAccess(uid, gid, pid, fd);
-            return;
+            return null;
         }
 
-        final Intent mIntent = createIntent(firstPackageName, uid, gid, pid, fd);
-        mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM);
+        return firstPackageName;
+
     }
 
     private void declineLogdAccess(int uid, int gid, int pid, int fd) {
@@ -198,16 +207,23 @@
 
                 final int procState = LocalServices.getService(ActivityManagerInternal.class)
                         .getUidProcessState(mUid);
-                // If the process is foreground, show a dialog for user consent
+                // If the process is foreground and we can retrieve the package name, show a dialog
+                // for user consent
                 if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
-                    showDialog(mUid, mGid, mPid, mFd);
-                } else {
-                    /**
-                     * If the process is background, decline the logd access.
-                     **/
-                    declineLogdAccess(mUid, mGid, mPid, mFd);
-                    return;
+                    String packageName = getPackageName(mUid, mGid, mPid, mFd);
+                    if (packageName != null) {
+                        final Intent mIntent = createIntent(packageName, mUid, mGid, mPid, mFd);
+                        mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM);
+                        return;
+                    }
                 }
+
+                /**
+                 * If the process is background or cannot retrieve the package name,
+                 * decline the logd access.
+                 **/
+                declineLogdAccess(mUid, mGid, mPid, mFd);
+                return;
             }
         }
     }
diff --git a/services/core/java/com/android/server/net/TEST_MAPPING b/services/core/java/com/android/server/net/TEST_MAPPING
index 02095eb..4ccf09e 100644
--- a/services/core/java/com/android/server/net/TEST_MAPPING
+++ b/services/core/java/com/android/server/net/TEST_MAPPING
@@ -2,12 +2,8 @@
   "presubmit-large": [
     {
       "name": "CtsHostsideNetworkTests",
-      "file_patterns": ["(/|^)NetworkPolicy[^/]*\\.java"],
       "options": [
         {
-          "include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests"
-        },
-        {
           "exclude-annotation": "androidx.test.filters.FlakyTest"
         },
         {
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index 2fe7913..ec6443d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -722,6 +722,11 @@
         return snapshot().getSharedUser(sharedUserAppId);
     }
 
+    @Override
+    public boolean isUidPrivileged(int uid) {
+        return snapshot().isUidPrivileged(uid);
+    }
+
     @NonNull
     @Override
     @Deprecated
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 003268b..f639655 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -195,7 +195,6 @@
     private TaskFragment mInTaskFragment;
     @VisibleForTesting
     boolean mAddingToTask;
-    private Task mReuseTask;
 
     private ActivityInfo mNewTaskInfo;
     private Intent mNewTaskIntent;
@@ -204,6 +203,7 @@
     // The task that the last activity was started into. We currently reset the actual start
     // activity's task and as a result may not have a reference to the task in all cases
     private Task mTargetTask;
+    private boolean mIsTaskCleared;
     private boolean mMovedToFront;
     private boolean mNoAnimation;
     private boolean mAvoidMoveToFront;
@@ -597,7 +597,6 @@
         mInTask = starter.mInTask;
         mInTaskFragment = starter.mInTaskFragment;
         mAddingToTask = starter.mAddingToTask;
-        mReuseTask = starter.mReuseTask;
 
         mNewTaskInfo = starter.mNewTaskInfo;
         mNewTaskIntent = starter.mNewTaskIntent;
@@ -605,6 +604,7 @@
 
         mTargetTask = starter.mTargetTask;
         mTargetRootTask = starter.mTargetRootTask;
+        mIsTaskCleared = starter.mIsTaskCleared;
         mMovedToFront = starter.mMovedToFront;
         mNoAnimation = starter.mNoAnimation;
         mAvoidMoveToFront = starter.mAvoidMoveToFront;
@@ -1568,10 +1568,7 @@
             return;
         }
 
-        final int clearTaskFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK;
-        boolean clearedTask = (mLaunchFlags & clearTaskFlags) == clearTaskFlags
-                && mReuseTask != null;
-        if (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP || clearedTask) {
+        if (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP) {
             // The activity was already running so it wasn't started, but either brought to the
             // front or the new intent was delivered to it since it was already in front. Notify
             // anyone interested in this piece of information.
@@ -1581,7 +1578,7 @@
             final ActivityRecord top = targetTask.getTopNonFinishingActivity();
             final boolean visible = top != null && top.isVisible();
             mService.getTaskChangeNotificationController().notifyActivityRestartAttempt(
-                    targetTask.getTaskInfo(), homeTaskVisible, clearedTask, visible);
+                    targetTask.getTaskInfo(), homeTaskVisible, mIsTaskCleared, visible);
         }
 
         if (ActivityManager.isStartResultSuccessful(result)) {
@@ -1927,6 +1924,7 @@
         return START_SUCCESS;
     }
 
+    /** Returns the leaf task where the target activity may be placed. */
     private Task computeTargetTask() {
         if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
                 && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
@@ -1935,6 +1933,12 @@
         } else if (mSourceRecord != null) {
             return mSourceRecord.getTask();
         } else if (mInTask != null) {
+            // The task is specified from AppTaskImpl, so it may not be attached yet.
+            if (!mInTask.isAttached()) {
+                // Attach the task to display area. Ignore the returned root task (though usually
+                // they are the same) because "target task" should be leaf task.
+                getOrCreateRootTask(mStartActivity, mLaunchFlags, mInTask, mOptions);
+            }
             return mInTask;
         } else {
             final Task rootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, null /* task */,
@@ -2225,13 +2229,12 @@
             // activity. Well that should not be too hard...
             // Note: we must persist the {@link Task} first as intentActivity could be
             // removed from calling performClearTaskLocked (For example, if it is being brought out
-            // of history or if it is finished immediately), thus disassociating the task. Also note
-            // that mReuseTask is reset as a result of {@link Task#performClearTaskLocked}
-            // launching another activity. Keep the task-overlay activity because the targetTask
-            // will be reused to launch new activity.
+            // of history or if it is finished immediately), thus disassociating the task. Keep the
+            // task-overlay activity because the targetTask will be reused to launch new activity.
             targetTask.performClearTaskForReuse(true /* excludingTaskOverlay*/);
             targetTask.setIntent(mStartActivity);
             mAddingToTask = true;
+            mIsTaskCleared = true;
         } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                 || isDocumentLaunchesIntoExisting(mLaunchFlags)
                 || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK,
@@ -2344,7 +2347,6 @@
         mInTask = null;
         mInTaskFragment = null;
         mAddingToTask = false;
-        mReuseTask = null;
 
         mNewTaskInfo = null;
         mNewTaskIntent = null;
@@ -2352,6 +2354,7 @@
 
         mTargetRootTask = null;
         mTargetTask = null;
+        mIsTaskCleared = false;
         mMovedToFront = false;
         mNoAnimation = false;
         mAvoidMoveToFront = false;
@@ -2569,8 +2572,6 @@
             } else {
                 mAddingToTask = true;
             }
-
-            mReuseTask = mInTask;
         } else {
             mInTask = null;
             // Launch ResolverActivity in the source task, so that it stays in the task bounds
@@ -2843,7 +2844,7 @@
                 mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
                 mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
         task.mTransitionController.collectExistenceChange(task);
-        addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
+        addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask");
 
         ProtoLog.v(WM_DEBUG_TASKS, "Starting new activity %s in new task %s",
                 mStartActivity, mStartActivity.getTask());
@@ -2953,11 +2954,6 @@
 
     private Task getOrCreateRootTask(ActivityRecord r, int launchFlags, Task task,
             ActivityOptions aOptions) {
-        // We are reusing a task, keep the root task!
-        if (mReuseTask != null) {
-            return mReuseTask.getRootTask();
-        }
-
         final boolean onTop =
                 (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
         final Task sourceTask = mSourceRecord != null ? mSourceRecord.getTask() : null;
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 22a2c41..6e46fa6 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -26,6 +26,8 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
 import android.os.UserHandle;
 
 /**
@@ -54,6 +56,16 @@
     }
 
     @Override
+    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+            throws RemoteException {
+        try {
+            return super.onTransact(code, data, reply, flags);
+        } catch (RuntimeException e) {
+            throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e);
+        }
+    }
+
+    @Override
     public void finishAndRemoveTask() {
         checkCaller();
 
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index ef0b737..66c625e 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -47,12 +47,6 @@
 class BackNavigationController {
 
     private static final String TAG = "BackNavigationController";
-    // By default, enable new back dispatching without any animations.
-    private static final int BACK_PREDICTABILITY_PROP =
-            SystemProperties.getInt("persist.debug.back_predictability", 1);
-    private static final int ANIMATIONS_MASK = 1 << 1;
-    private static final int SCREENSHOT_MASK = 1 << 2;
-
     @Nullable
     private TaskSnapshotController mTaskSnapshotController;
 
@@ -60,15 +54,15 @@
      * Returns true if the back predictability feature is enabled
      */
     static boolean isEnabled() {
-        return BACK_PREDICTABILITY_PROP > 0;
+        return SystemProperties.getInt("persist.wm.debug.predictive_back", 1) != 0;
     }
 
     static boolean isScreenshotEnabled() {
-        return (BACK_PREDICTABILITY_PROP & SCREENSHOT_MASK) != 0;
+        return SystemProperties.getInt("persist.wm.debug.predictive_back_screenshot", 0) != 0;
     }
 
     private static boolean isAnimationEnabled() {
-        return (BACK_PREDICTABILITY_PROP & ANIMATIONS_MASK) != 0;
+        return SystemProperties.getInt("persist.wm.debug.predictive_back_anim", 0) != 0;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 81560d4..0893207 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1992,7 +1992,8 @@
         scheduleAnimation();
 
         forAllWindows(w -> {
-            if (w.mHasSurface && !rotateSeamlessly) {
+            if (!w.mHasSurface) return;
+            if (!rotateSeamlessly) {
                 ProtoLog.v(WM_DEBUG_ORIENTATION, "Set mOrientationChanging of %s", w);
                 w.setOrientationChanging(true);
             }
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 0038c71..c162e8e 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -175,7 +175,7 @@
             final Rect spaceToFill = transformedBounds != null
                     ? transformedBounds
                     : mActivityRecord.inMultiWindowMode()
-                            ? mActivityRecord.getRootTask().getBounds()
+                            ? mActivityRecord.getTask().getBounds()
                             : mActivityRecord.getRootTask().getParent().getBounds();
             mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
         } else if (mLetterbox != null) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 51d68bc..2379822 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2606,11 +2606,17 @@
                     return;
                 }
 
+                // Remove immediately if there is display transition because the animation is
+                // usually unnoticeable (e.g. covered by rotation animation) and the animation
+                // bounds could be inconsistent, such as depending on when the window applies
+                // its draw transaction with new rotation.
+                final boolean allowExitAnimation = !getDisplayContent().inTransition();
+
                 if (wasVisible) {
                     final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
 
                     // Try starting an animation.
-                    if (mWinAnimator.applyAnimationLocked(transit, false)) {
+                    if (allowExitAnimation && mWinAnimator.applyAnimationLocked(transit, false)) {
                         ProtoLog.v(WM_DEBUG_ANIM,
                                 "Set animatingExit: reason=remove/applyAnimation win=%s", this);
                         mAnimatingExit = true;
@@ -2624,7 +2630,8 @@
                         mWmService.mAccessibilityController.onWindowTransition(this, transit);
                     }
                 }
-                final boolean isAnimating = mAnimatingExit || isExitAnimationRunningSelfOrParent();
+                final boolean isAnimating = allowExitAnimation
+                        && (mAnimatingExit || isExitAnimationRunningSelfOrParent());
                 final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null
                         && mActivityRecord.isLastWindow(this);
                 // We delay the removal of a window if it has a showing surface that can be used to run
@@ -3602,6 +3609,10 @@
         mAnimatingExit = false;
         ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=destroySurface win=%s", this);
 
+        // Clear the flag so the buffer requested for the next new surface won't be dropped by
+        // mistaking the surface size needs to update.
+        mReportOrientationChanged = false;
+
         if (useBLASTSync()) {
             immediatelyNotifyBlastSync();
         }
@@ -5284,12 +5295,6 @@
         if (mControllableInsetProvider != null) {
             return;
         }
-        if (getDisplayContent().inTransition()) {
-            // Skip because the animation is usually unnoticeable (e.g. covered by rotation
-            // animation) and the animation bounds could be inconsistent, such as depending
-            // on when the window applies its draw transaction with new rotation.
-            return;
-        }
 
         final DisplayInfo displayInfo = getDisplayInfo();
         anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index c9523ec..529def3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -27,6 +27,7 @@
 import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
 import static android.app.AlarmManager.RTC;
 import static android.app.AlarmManager.RTC_WAKEUP;
+import static android.app.AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT;
 import static android.app.AlarmManager.WINDOW_EXACT;
 import static android.app.AlarmManager.WINDOW_HEURISTIC;
 import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -122,6 +123,7 @@
 import android.app.IAlarmManager;
 import android.app.PendingIntent;
 import android.app.compat.CompatChanges;
+import android.app.role.RoleManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -184,6 +186,7 @@
 import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -232,6 +235,8 @@
     @Mock
     private PackageManagerInternal mPackageManagerInternal;
     @Mock
+    private RoleManager mRoleManager;
+    @Mock
     private AppStateTrackerImpl mAppStateTracker;
     @Mock
     private AlarmManagerService.ClockReceiver mClockReceiver;
@@ -457,6 +462,7 @@
 
         when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
         when(mMockContext.getSystemService(BatteryManager.class)).thenReturn(mBatteryManager);
+        when(mMockContext.getSystemService(RoleManager.class)).thenReturn(mRoleManager);
 
         registerAppIds(new String[]{TEST_CALLING_PACKAGE},
                 new Integer[]{UserHandle.getAppId(TEST_CALLING_UID)});
@@ -3191,6 +3197,70 @@
     }
 
     @Test
+    public void isScheduleExactAlarmAllowedByDefault() {
+        final String package1 = "priv";
+        final String package2 = "signed";
+        final String package3 = "normal";
+        final String package4 = "wellbeing";
+        final int uid1 = 1294;
+        final int uid2 = 8321;
+        final int uid3 = 3412;
+        final int uid4 = 4591;
+
+        when(mPackageManagerInternal.isUidPrivileged(uid1)).thenReturn(true);
+        when(mPackageManagerInternal.isUidPrivileged(uid2)).thenReturn(false);
+        when(mPackageManagerInternal.isUidPrivileged(uid3)).thenReturn(false);
+        when(mPackageManagerInternal.isUidPrivileged(uid4)).thenReturn(false);
+
+        when(mPackageManagerInternal.isPlatformSigned(package1)).thenReturn(false);
+        when(mPackageManagerInternal.isPlatformSigned(package2)).thenReturn(true);
+        when(mPackageManagerInternal.isPlatformSigned(package3)).thenReturn(false);
+        when(mPackageManagerInternal.isPlatformSigned(package4)).thenReturn(false);
+
+        when(mRoleManager.getRoleHolders(RoleManager.ROLE_SYSTEM_WELLBEING)).thenReturn(
+                Arrays.asList(package4));
+
+        mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true);
+        mService.mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = false;
+        mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[] {
+                package1,
+                package3,
+        });
+
+        // Deny listed packages will be false.
+        assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1));
+        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2));
+        assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3));
+        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4));
+
+        mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, false);
+        mService.mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = true;
+        mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[] {
+                package1,
+                package3,
+        });
+
+        // Same as above, deny listed packages will be false.
+        assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1));
+        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2));
+        assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3));
+        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4));
+
+        mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true);
+        mService.mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = true;
+        mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[] {
+                package1,
+                package3,
+        });
+
+        // Deny list doesn't matter now, only exemptions should be true.
+        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1));
+        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2));
+        assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3));
+        assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4));
+    }
+
+    @Test
     public void alarmScheduledAtomPushed() {
         for (int i = 0; i < 10; i++) {
             final PendingIntent pi = getNewMockPendingIntent();
diff --git a/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml b/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml
index 0c531de..a4558ac 100644
--- a/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml
+++ b/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml
@@ -18,4 +18,5 @@
     android:accountType="@string/test_account_type1"
     android:icon="@drawable/icon1"
     android:smallIcon="@drawable/icon1"
+    android:customTokens="true"
     android:label="@string/test_account_type1_authenticator_label" />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index 3c2fbd9..863dcb6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -18,8 +18,11 @@
 
 import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
 import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_WINDOW;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -255,6 +258,20 @@
     }
 
     @Test
+    public void getCurrentMode_changeOtherDisplayMode_returnDefaultModeOnDefaultDisplay() {
+        final int otherDisplayId = TEST_DISPLAY + 1;
+        setMagnificationActivated(otherDisplayId, MAGNIFICATION_MODE_WINDOW);
+
+        int currentMode = mMagnificationProcessor.getControllingMode(TEST_DISPLAY);
+
+        assertFalse(mMockMagnificationController.isActivated(TEST_DISPLAY,
+                ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN));
+        assertFalse(mMockMagnificationController.isActivated(TEST_DISPLAY,
+                ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
+        assertEquals(MAGNIFICATION_MODE_FULLSCREEN, currentMode);
+    }
+
+    @Test
     public void resetFullscreenMagnification_fullscreenMagnificationActivated() {
         setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index cc6d761..ca22f80 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -555,7 +555,22 @@
     }
 
     @Test
-    public void onRectangleOnScreenRequested_NoneIsActivated_noneDispatchEvent() {
+    public void onRectangleOnScreenRequested_noneIsActivated_noneDispatchEvent() {
+        UiChangesForAccessibilityCallbacks callbacks = getUiChangesForAccessibilityCallbacks();
+
+        callbacks.onRectangleOnScreenRequested(TEST_DISPLAY,
+                TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom);
+
+        verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(
+                eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt());
+        verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+                anyInt(), anyInt(), anyInt(), anyInt());
+    }
+
+    @Test
+    public void onRectangleOnScreenRequested_otherDisplayIsActivated_noneEventOnDefaultDisplay() {
+        mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY + 1,
+                true);
         UiChangesForAccessibilityCallbacks callbacks = getUiChangesForAccessibilityCallbacks();
 
         callbacks.onRectangleOnScreenRequested(TEST_DISPLAY,
@@ -579,6 +594,41 @@
     }
 
     @Test
+    public void getLastActivatedMode_switchMode_returnExpectedLastActivatedMode()
+            throws RemoteException {
+        activateMagnifier(MODE_WINDOW, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
+
+        final int lastActivatedMode = mMagnificationController
+                .getLastMagnificationActivatedMode(TEST_DISPLAY);
+
+        assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, lastActivatedMode);
+    }
+
+    @Test
+    public void getLastActivatedMode_switchModeAtOtherDisplay_returnExpectedLastActivatedMode()
+            throws RemoteException {
+        activateMagnifier(TEST_DISPLAY, MODE_WINDOW, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
+        activateMagnifier(TEST_DISPLAY + 1, MODE_FULLSCREEN, MAGNIFIED_CENTER_X,
+                MAGNIFIED_CENTER_Y);
+
+        final int lastActivatedMode = mMagnificationController
+                .getLastMagnificationActivatedMode(TEST_DISPLAY);
+
+        assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, lastActivatedMode);
+    }
+
+    @Test
+    public void getLastActivatedMode_otherDisplayIsActivated_defaultModeOnDefaultDisplay()
+            throws RemoteException {
+        activateMagnifier(TEST_DISPLAY + 1, MODE_WINDOW, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
+
+        int lastActivatedMode = mMagnificationController
+                .getLastMagnificationActivatedMode(TEST_DISPLAY);
+
+        assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, lastActivatedMode);
+    }
+
+    @Test
     public void onFullScreenMagnificationActivationState_fullScreenEnabled_logFullScreenDuration() {
         MagnificationController spyController = spy(mMagnificationController);
         spyController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
@@ -818,17 +868,22 @@
     }
 
     private void activateMagnifier(int mode, float centerX, float centerY) throws RemoteException {
+        activateMagnifier(TEST_DISPLAY, mode, centerX, centerY);
+    }
+
+    private void activateMagnifier(int displayId, int mode, float centerX, float centerY)
+            throws RemoteException {
         final boolean windowMagnifying = mWindowMagnificationManager.isWindowMagnifierEnabled(
-                TEST_DISPLAY);
+                displayId);
         if (windowMagnifying) {
-            mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+            mWindowMagnificationManager.disableWindowMagnification(displayId, false);
             mMockConnection.invokeCallbacks();
         }
         if (mode == MODE_FULLSCREEN) {
-            mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, centerX,
+            mScreenMagnificationController.setScaleAndCenter(displayId, DEFAULT_SCALE, centerX,
                     centerY, true, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
         } else {
-            mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
+            mWindowMagnificationManager.enableWindowMagnification(displayId, DEFAULT_SCALE,
                     centerX, centerY, null, TEST_SERVICE_ID);
             mMockConnection.invokeCallbacks();
         }
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 997a138..d5c5745 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -26,9 +26,11 @@
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.nullable;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.accounts.AbstractAccountAuthenticator;
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.AccountManagerInternal;
@@ -1698,13 +1700,14 @@
 
         final CountDownLatch latch = new CountDownLatch(1);
         Response response = new Response(latch, mMockAccountManagerResponse);
+        long expiryEpochTimeInMillis = System.currentTimeMillis() + ONE_DAY_IN_MILLISECOND;
         mAms.getAuthToken(
                     response, // response
                     AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
                     "authTokenType", // authTokenType
                     true, // notifyOnAuthFailure
                     false, // expectActivityLaunch
-                    createGetAuthTokenOptions());
+                createGetAuthTokenOptionsWithExpiry(expiryEpochTimeInMillis));
         waitForLatch(latch);
 
         verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
@@ -1715,6 +1718,58 @@
                 AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS);
         assertEquals(result.getString(AccountManager.KEY_ACCOUNT_TYPE),
                 AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+        assertEquals(result.getLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY),
+                expiryEpochTimeInMillis);
+    }
+
+    @SmallTest
+    public void testGetAuthTokenCachedSuccess() throws Exception {
+        unlockSystemUser();
+        when(mMockContext.createPackageContextAsUser(
+                anyString(), anyInt(), any(UserHandle.class))).thenReturn(mMockContext);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        String[] list = new String[]{AccountManagerServiceTestFixtures.CALLER_PACKAGE};
+        when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        long expiryEpochTimeInMillis = System.currentTimeMillis() + ONE_DAY_IN_MILLISECOND;
+        mAms.getAuthToken(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                "authTokenType", // authTokenType
+                true, // notifyOnAuthFailure
+                false, // expectActivityLaunch
+                createGetAuthTokenOptionsWithExpiry(expiryEpochTimeInMillis));
+        waitForLatch(latch);
+
+        // Make call for cached token.
+        mAms.getAuthToken(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                "authTokenType", // authTokenType
+                true, // notifyOnAuthFailure
+                false, // expectActivityLaunch
+                createGetAuthTokenOptionsWithExpiry(expiryEpochTimeInMillis + 10));
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse, times(2)).onResult(mBundleCaptor.capture());
+        List<Bundle> result = mBundleCaptor.getAllValues();
+        assertGetTokenResponse(result.get(0), expiryEpochTimeInMillis);
+        // cached token was returned with the same expiration time as first token.
+        assertGetTokenResponse(result.get(1), expiryEpochTimeInMillis);
+    }
+
+    private void assertGetTokenResponse(Bundle result, long expiryEpochTimeInMillis) {
+        assertEquals(result.getString(AccountManager.KEY_AUTHTOKEN),
+                AccountManagerServiceTestFixtures.AUTH_TOKEN);
+        assertEquals(result.getString(AccountManager.KEY_ACCOUNT_NAME),
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS);
+        assertEquals(result.getString(AccountManager.KEY_ACCOUNT_TYPE),
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+        assertEquals(result.getLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY),
+                expiryEpochTimeInMillis);
+
     }
 
     @SmallTest
@@ -3241,11 +3296,16 @@
     }
 
     private Bundle createGetAuthTokenOptions() {
+        return createGetAuthTokenOptionsWithExpiry(
+                System.currentTimeMillis() + ONE_DAY_IN_MILLISECOND);
+    }
+
+    private Bundle createGetAuthTokenOptionsWithExpiry(long expiryEpochTimeInMillis) {
         Bundle options = new Bundle();
         options.putString(AccountManager.KEY_ANDROID_PACKAGE_NAME,
                 AccountManagerServiceTestFixtures.CALLER_PACKAGE);
         options.putLong(AccountManagerServiceTestFixtures.KEY_TOKEN_EXPIRY,
-                System.currentTimeMillis() + ONE_DAY_IN_MILLISECOND);
+                expiryEpochTimeInMillis);
         return options;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index 33ea710..b9ae670 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -25,6 +25,8 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
 
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+
 import static junit.framework.Assert.assertEquals;
 
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -48,6 +50,7 @@
 import android.os.Process;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
+import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
@@ -370,6 +373,19 @@
                 mUserId);
     }
 
+    @Test
+    public void testRequireStrongAuth_STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN() throws Exception {
+        when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
+
+        assertEquals(0, mCommand.exec(new Binder(), in, out, err,
+                new String[] { "require-strong-auth", "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN"},
+                mShellCallback, mResultReceiver));
+
+        verify(mLockPatternUtils).requireStrongAuth(
+                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
+                UserHandle.USER_ALL);
+    }
+
     private List<LockPatternView.Cell> stringToPattern(String str) {
         return LockPatternUtils.byteArrayToPattern(str.getBytes());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 9902e83..908de34 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1139,18 +1139,8 @@
                 true /* createdByOrganizer */);
         sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
 
-        starter.startActivityInner(
-                /* r */targetRecord,
-                /* sourceRecord */ sourceRecord,
-                /* voiceSession */null,
-                /* voiceInteractor */ null,
-                /* startFlags */ 0,
-                /* doResume */true,
-                /* options */null,
-                /* inTask */null,
-                /* inTaskFragment */ taskFragment,
-                /* restrictedBgActivity */false,
-                /* intentGrants */null);
+        startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+                null /* inTask */, taskFragment);
 
         assertFalse(taskFragment.hasChild());
     }
@@ -1167,18 +1157,8 @@
         taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class), SYSTEM_UID,
                 "system_uid");
 
-        starter.startActivityInner(
-                /* r */targetRecord,
-                /* sourceRecord */ sourceRecord,
-                /* voiceSession */null,
-                /* voiceInteractor */ null,
-                /* startFlags */ 0,
-                /* doResume */true,
-                /* options */null,
-                /* inTask */null,
-                /* inTaskFragment */ taskFragment,
-                /* restrictedBgActivity */false,
-                /* intentGrants */null);
+        startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+                null /* inTask */, taskFragment);
 
         assertTrue(taskFragment.hasChild());
     }
@@ -1195,18 +1175,8 @@
         taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class),
                 targetRecord.getUid(), "test_process_name");
 
-        starter.startActivityInner(
-                /* r */targetRecord,
-                /* sourceRecord */ sourceRecord,
-                /* voiceSession */null,
-                /* voiceInteractor */ null,
-                /* startFlags */ 0,
-                /* doResume */true,
-                /* options */null,
-                /* inTask */null,
-                /* inTaskFragment */ taskFragment,
-                /* restrictedBgActivity */false,
-                /* intentGrants */null);
+        startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+                null /* inTask */, taskFragment);
 
         assertTrue(taskFragment.hasChild());
     }
@@ -1231,18 +1201,8 @@
         doReturn(true).when(signingDetails).hasAncestorOrSelfWithDigest(any());
         doReturn(signingDetails).when(androidPackage).getSigningDetails();
 
-        starter.startActivityInner(
-                /* r */targetRecord,
-                /* sourceRecord */ sourceRecord,
-                /* voiceSession */null,
-                /* voiceInteractor */ null,
-                /* startFlags */ 0,
-                /* doResume */true,
-                /* options */null,
-                /* inTask */null,
-                /* inTaskFragment */ taskFragment,
-                /* restrictedBgActivity */false,
-                /* intentGrants */null);
+        startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+                null /* inTask */, taskFragment);
 
         assertTrue(taskFragment.hasChild());
     }
@@ -1258,23 +1218,30 @@
 
         targetRecord.info.flags |= ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
 
-        starter.startActivityInner(
-                /* r */targetRecord,
-                /* sourceRecord */ sourceRecord,
-                /* voiceSession */null,
-                /* voiceInteractor */ null,
-                /* startFlags */ 0,
-                /* doResume */true,
-                /* options */null,
-                /* inTask */null,
-                /* inTaskFragment */ taskFragment,
-                /* restrictedBgActivity */false,
-                /* intentGrants */null);
+        startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+                null /* inTask */, taskFragment);
 
         assertTrue(taskFragment.hasChild());
     }
 
     @Test
+    public void testStartActivityInner_inTask() {
+        final ActivityStarter starter = prepareStarter(0, false);
+        // Simulate an app uses AppTask to create a non-attached task, and then it requests to
+        // start activity in the task.
+        final Task inTask = new TaskBuilder(mSupervisor).setTaskDisplayArea(null).setTaskId(123)
+                .build();
+        inTask.inRecents = true;
+        assertFalse(inTask.isAttached());
+        final ActivityRecord target = new ActivityBuilder(mAtm).build();
+        startActivityInner(starter, target, null /* source */, null /* options */, inTask,
+                null /* inTaskFragment */);
+
+        assertTrue(inTask.isAttached());
+        assertEquals(inTask, target.getTask());
+    }
+
+    @Test
     public void testLaunchCookie_newAndExistingTask() {
         final ActivityStarter starter = prepareStarter(0, false);
 
@@ -1322,21 +1289,20 @@
 
         // Start the target launch-into-pip activity from a source
         final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        starter.startActivityInner(
-                /* r */ targetRecord,
-                /* sourceRecord */ sourceRecord,
-                /* voiceSession */ null,
-                /* voiceInteractor */ null,
-                /* startFlags */ 0,
-                /* doResume */ true,
-                /* options */ opts,
-                /* inTask */ null,
-                /* inTaskFragment */ null,
-                /* restrictedBgActivity */ false,
-                /* intentGrants */ null);
+        startActivityInner(starter, targetRecord, sourceRecord, opts,
+                null /* inTask */, null /* inTaskFragment */);
 
         // Verify the ActivityRecord#getLaunchIntoPipHostActivity points to sourceRecord.
         assertThat(targetRecord.getLaunchIntoPipHostActivity()).isNotNull();
         assertEquals(targetRecord.getLaunchIntoPipHostActivity(), sourceRecord);
     }
+
+    private static void startActivityInner(ActivityStarter starter, ActivityRecord target,
+            ActivityRecord source, ActivityOptions options, Task inTask,
+            TaskFragment inTaskFragment) {
+        starter.startActivityInner(target, source, null /* voiceSession */,
+                null /* voiceInteractor */, 0 /* startFlags */, true /* doResume */,
+                options, inTask, inTaskFragment, false /* restrictedBgActivity */,
+                null /* intentGrants */);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
similarity index 74%
rename from services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
index f968999..a8282600 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.START_ABORTED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
@@ -26,6 +27,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
 
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
@@ -45,13 +47,13 @@
 import java.util.Set;
 
 /**
- * Tests for the {@link DisplayWindowPolicyControllerHelper} class.
+ * Tests for the {@link DisplayWindowPolicyController} class.
  *
  * Build/Install/Run:
- *  atest WmTests:DisplayWindowPolicyControllerHelperTests
+ *  atest WmTests:DisplayWindowPolicyControllerTests
  */
 @RunWith(WindowTestRunner.class)
-public class DisplayWindowPolicyControllerHelperTests extends WindowTestsBase {
+public class DisplayWindowPolicyControllerTests extends WindowTestsBase {
     private static final int TEST_USER_0_ID = 0;
     private static final int TEST_USER_1_ID = 10;
 
@@ -152,8 +154,51 @@
         assertTrue(mSecondaryDisplay.mDwpcHelper.isWindowingModeSupported(WINDOWING_MODE_PINNED));
     }
 
+    @Test
+    public void testInterestedWindowFlags() {
+        final int fakeFlag1 = 0x00000010;
+        final int fakeFlag2 = 0x00000100;
+        final int fakeSystemFlag1 = 0x00000010;
+        final int fakeSystemFlag2 = 0x00000100;
+
+        mDwpc.setInterestedWindowFlags(fakeFlag1, fakeSystemFlag1);
+
+        assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag1, fakeSystemFlag1));
+        assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag1, fakeSystemFlag2));
+        assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag2, fakeSystemFlag1));
+        assertFalse(mDwpc.isInterestedWindowFlags(fakeFlag2, fakeSystemFlag2));
+    }
+
+    @Test
+    public void testCanContainActivities() {
+        ActivityStarter starter = new ActivityStarter(mock(ActivityStartController.class), mAtm,
+                mSupervisor, mock(ActivityStartInterceptor.class));
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(mSecondaryDisplay).build();
+        final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setTask(task).build();
+        final ActivityRecord disallowedRecord =
+                new ActivityBuilder(mAtm).setComponent(mDwpc.DISALLOWED_ACTIVITY).build();
+
+        int result = starter.startActivityInner(
+                disallowedRecord,
+                sourceRecord,
+                /* voiceSession */null,
+                /* voiceInteractor */ null,
+                /* startFlags */ 0,
+                /* doResume */true,
+                /* options */null,
+                /* inTask */null,
+                /* inTaskFragment */ null,
+                /* restrictedBgActivity */false,
+                /* intentGrants */null);
+
+        assertEquals(result, START_ABORTED);
+    }
+
     private class TestDisplayWindowPolicyController extends DisplayWindowPolicyController {
 
+        public ComponentName DISALLOWED_ACTIVITY =
+                new ComponentName("fake.package", "DisallowedActivity");
+
         ComponentName mTopActivity = null;
         int mTopActivityUid = UserHandle.USER_NULL;
         ArraySet<Integer> mRunningUids = new ArraySet<>();
@@ -161,7 +206,14 @@
         @Override
         public boolean canContainActivities(@NonNull List<ActivityInfo> activities,
                 @WindowConfiguration.WindowingMode int windowingMode) {
-            return false;
+            final int activityCount = activities.size();
+            for (int i = 0; i < activityCount; i++) {
+                final ActivityInfo aInfo = activities.get(i);
+                if (aInfo.getComponentName().equals(DISALLOWED_ACTIVITY)) {
+                    return false;
+                }
+            }
+            return true;
         }
 
         @Override
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index 3843a62..249f740 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -21,6 +21,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.telephony.Rlog;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -227,7 +230,6 @@
         this.mIccIdAccessRestricted = iccIdAccessRestricted;
     }
 
-
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
@@ -261,7 +263,7 @@
                 + ", mCardId="
                 + mCardId
                 + ", mEid="
-                + mEid
+                + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid)
                 + ", mPhysicalSlotIndex="
                 + mPhysicalSlotIndex
                 + ", mIsRemovable="
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 17ce450..dd3639a 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -279,7 +279,7 @@
                 + ", mIsEuicc="
                 + mIsEuicc
                 + ", mCardId="
-                + mCardId
+                + SubscriptionInfo.givePrintableIccid(mCardId)
                 + ", cardState="
                 + mCardStateInfo
                 + ", mIsExtendedApduSupported="
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
index 7f49663..1b60403 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -79,7 +79,7 @@
     /**
      * Checks that [FlickerComponentName.IME] layer is visible at the end of the transition
      */
-    @Presubmit
+    @FlakyTest(bugId = 227142436)
     @Test
     fun imeLayerExistsEnd() {
         testSpec.assertLayersEnd {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
index 9c9dedc2..4b8a8c8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker.quickswitch
 
 import android.platform.test.annotations.RequiresDevice
+import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.annotation.Group1
@@ -44,6 +45,7 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group1
+@FlakyTest(bugId = 228009808)
 open class QuickSwitchBetweenTwoAppsForwardTest_ShellTransit(testSpec: FlickerTestParameter)
     : QuickSwitchBetweenTwoAppsForwardTest(testSpec) {
     @Before
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index bd0a4bc..bfb3285 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -165,6 +165,7 @@
     ],
     proto: {
         export_proto_headers: true,
+        type: "full",
     },
     defaults: ["aapt2_defaults"],
 }