Merge "Fix dialog accessibility issues." into sc-v2-dev
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 8062832..7e38287 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -518,7 +518,9 @@
     public static final int GLOBAL_ACTION_POWER_DIALOG = 6;
 
     /**
-     * Action to toggle docking the current app's window
+     * Action to toggle docking the current app's window.
+     * <p>
+     * <strong>Note:</strong>  It is effective only if it appears in {@link #getSystemActions()}.
      */
     public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7;
 
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index edcab29..0ff9f66 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -431,7 +431,7 @@
     private boolean mOverrideTaskTransition;
     private String mSplashScreenThemeResName;
     @SplashScreen.SplashScreenStyle
-    private int mSplashScreenStyle;
+    private int mSplashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
     private boolean mRemoveWithTaskOrganizer;
     private boolean mLaunchedFromBubble;
     private boolean mTransientLaunch;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 45df0d9..46f6597 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -321,7 +321,8 @@
 
     @UnsupportedAppUsage
     private ContextImpl mSystemContext;
-    private final SparseArray<ContextImpl> mDisplaySystemUiContexts = new SparseArray<>();
+    @GuardedBy("this")
+    private SparseArray<ContextImpl> mDisplaySystemUiContexts;
 
     @UnsupportedAppUsage
     static volatile IPackageManager sPackageManager;
@@ -2650,7 +2651,6 @@
         }
     }
 
-    @Override
     @NonNull
     public ContextImpl getSystemUiContext() {
         return getSystemUiContext(DEFAULT_DISPLAY);
@@ -2664,6 +2664,9 @@
     @NonNull
     public ContextImpl getSystemUiContext(int displayId) {
         synchronized (this) {
+            if (mDisplaySystemUiContexts == null) {
+                mDisplaySystemUiContexts = new SparseArray<>();
+            }
             ContextImpl systemUiContext = mDisplaySystemUiContexts.get(displayId);
             if (systemUiContext == null) {
                 systemUiContext = ContextImpl.createSystemUiContext(getSystemContext(), displayId);
@@ -2673,6 +2676,25 @@
         }
     }
 
+    @Nullable
+    @Override
+    public ContextImpl getSystemUiContextNoCreate() {
+        synchronized (this) {
+            if (mDisplaySystemUiContexts == null) return null;
+            return mDisplaySystemUiContexts.get(DEFAULT_DISPLAY);
+        }
+    }
+
+    void onSystemUiContextCleanup(ContextImpl context) {
+        synchronized (this) {
+            if (mDisplaySystemUiContexts == null) return;
+            final int index = mDisplaySystemUiContexts.indexOfValue(context);
+            if (index >= 0) {
+                mDisplaySystemUiContexts.removeAt(index);
+            }
+        }
+    }
+
     public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
         synchronized (this) {
             getSystemContext().installSystemApplicationInfo(info, classLoader);
diff --git a/core/java/android/app/ActivityThreadInternal.java b/core/java/android/app/ActivityThreadInternal.java
index bc698f6..b9ad5c3 100644
--- a/core/java/android/app/ActivityThreadInternal.java
+++ b/core/java/android/app/ActivityThreadInternal.java
@@ -28,7 +28,7 @@
 interface ActivityThreadInternal {
     ContextImpl getSystemContext();
 
-    ContextImpl getSystemUiContext();
+    ContextImpl getSystemUiContextNoCreate();
 
     boolean isInDensityCompatMode();
 
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 8637e31..58f60a6 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -154,9 +154,12 @@
         int configDiff;
         boolean equivalent;
 
+        // Get theme outside of synchronization to avoid nested lock.
+        final Resources.Theme systemTheme = mActivityThread.getSystemContext().getTheme();
+        final ContextImpl systemUiContext = mActivityThread.getSystemUiContextNoCreate();
+        final Resources.Theme systemUiTheme =
+                systemUiContext != null ? systemUiContext.getTheme() : null;
         synchronized (mResourcesManager) {
-            final Resources.Theme systemTheme = mActivityThread.getSystemContext().getTheme();
-            final Resources.Theme systemUiTheme = mActivityThread.getSystemUiContext().getTheme();
             if (mPendingConfiguration != null) {
                 if (!mPendingConfiguration.isOtherSeqNewer(config)) {
                     config = mPendingConfiguration;
@@ -207,7 +210,8 @@
                 systemTheme.rebase();
             }
 
-            if ((systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
+            if (systemUiTheme != null
+                    && (systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
                 systemUiTheme.rebase();
             }
         }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index db3c7d9..fc1884a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3191,6 +3191,10 @@
     final void performFinalCleanup(String who, String what) {
         //Log.i(TAG, "Cleanup up context: " + this);
         mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
+        if (mContextType == CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
+                && mToken instanceof WindowTokenClient) {
+            mMainThread.onSystemUiContextCleanup(this);
+        }
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9605cbd..2c02be7 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5810,6 +5810,7 @@
                     p, result);
             buildCustomContentIntoTemplate(mContext, standard, customContent,
                     p, result);
+            makeHeaderExpanded(standard);
             return standard;
         }
 
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index b5298fc..d1ef591 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1010,12 +1010,12 @@
      * This change id restricts treatments that force a given min aspect ratio to activities
      * whose orientation is fixed to portrait.
      *
-     * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled.
+     * This treatment is enabled by default and only takes effect if OVERRIDE_MIN_ASPECT_RATIO is
+     * also enabled.
      * @hide
      */
     @ChangeId
     @Overridable
-    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S_V2)
     @TestApi
     public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // buganizer id
 
@@ -1367,18 +1367,18 @@
      * Returns if the activity should never be sandboxed to the activity window bounds.
      * @hide
      */
-    public boolean neverSandboxDisplayApis() {
+    public boolean neverSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) {
         return isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS)
-                || ConstrainDisplayApisConfig.neverConstrainDisplayApis(applicationInfo);
+                || constrainDisplayApisConfig.getNeverConstrainDisplayApis(applicationInfo);
     }
 
     /**
      * Returns if the activity should always be sandboxed to the activity window bounds.
      * @hide
      */
-    public boolean alwaysSandboxDisplayApis() {
+    public boolean alwaysSandboxDisplayApis(ConstrainDisplayApisConfig constrainDisplayApisConfig) {
         return isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS)
-                || ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(applicationInfo);
+                || constrainDisplayApisConfig.getAlwaysConstrainDisplayApis(applicationInfo);
     }
 
     /** @hide */
diff --git a/core/java/android/content/pm/ConstrainDisplayApisConfig.java b/core/java/android/content/pm/ConstrainDisplayApisConfig.java
index 11ba3d4..98b73aa 100644
--- a/core/java/android/content/pm/ConstrainDisplayApisConfig.java
+++ b/core/java/android/content/pm/ConstrainDisplayApisConfig.java
@@ -19,10 +19,15 @@
 import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
 
 import android.provider.DeviceConfig;
+import android.util.ArrayMap;
+import android.util.Pair;
 import android.util.Slog;
 
+import com.android.internal.os.BackgroundThread;
+
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Class for processing flags in the Device Config namespace 'constrain_display_apis'.
@@ -55,19 +60,45 @@
             "always_constrain_display_apis";
 
     /**
+     * Indicates that display APIs should never be constrained to the activity window bounds for all
+     * packages.
+     */
+    private boolean mNeverConstrainDisplayApisAllPackages;
+
+    /**
+     * Indicates that display APIs should never be constrained to the activity window bounds for
+     * a set of defined packages. Map keys are package names, and entries are a
+     * 'Pair(<min-version-code>, <max-version-code>)'.
+     */
+    private ArrayMap<String, Pair<Long, Long>> mNeverConstrainConfigMap;
+
+    /**
+     * Indicates that display APIs should always be constrained to the activity window bounds for
+     * a set of defined packages. Map keys are package names, and entries are a
+     * 'Pair(<min-version-code>, <max-version-code>)'.
+     */
+    private ArrayMap<String, Pair<Long, Long>> mAlwaysConstrainConfigMap;
+
+    public ConstrainDisplayApisConfig() {
+        updateCache();
+
+        DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+                BackgroundThread.getExecutor(), properties -> updateCache());
+    }
+
+    /**
      * Returns true if either the flag 'never_constrain_display_apis_all_packages' is true or the
      * flag 'never_constrain_display_apis' contains a package entry that matches the given {@code
      * applicationInfo}.
      *
      * @param applicationInfo Information about the application/package.
      */
-    public static boolean neverConstrainDisplayApis(ApplicationInfo applicationInfo) {
-        if (DeviceConfig.getBoolean(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
-                FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false)) {
+    public boolean getNeverConstrainDisplayApis(ApplicationInfo applicationInfo) {
+        if (mNeverConstrainDisplayApisAllPackages) {
             return true;
         }
 
-        return flagHasMatchingPackageEntry(FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, applicationInfo);
+        return flagHasMatchingPackageEntry(mNeverConstrainConfigMap, applicationInfo);
     }
 
     /**
@@ -76,73 +107,106 @@
      *
      * @param applicationInfo Information about the application/package.
      */
-    public static boolean alwaysConstrainDisplayApis(ApplicationInfo applicationInfo) {
-        return flagHasMatchingPackageEntry(FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, applicationInfo);
+    public boolean getAlwaysConstrainDisplayApis(ApplicationInfo applicationInfo) {
+        return flagHasMatchingPackageEntry(mAlwaysConstrainConfigMap, applicationInfo);
+    }
+
+
+    /**
+     * Updates {@link #mNeverConstrainDisplayApisAllPackages}, {@link #mNeverConstrainConfigMap},
+     * and {@link #mAlwaysConstrainConfigMap} from the {@link DeviceConfig}.
+     */
+    private void updateCache() {
+        mNeverConstrainDisplayApisAllPackages = DeviceConfig.getBoolean(
+                NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+                FLAG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES, /* defaultValue= */ false);
+
+        final String neverConstrainConfigStr = DeviceConfig.getString(
+                NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+                FLAG_NEVER_CONSTRAIN_DISPLAY_APIS, /* defaultValue= */ "");
+        mNeverConstrainConfigMap = buildConfigMap(neverConstrainConfigStr);
+
+        final String alwaysConstrainConfigStr = DeviceConfig.getString(
+                NAMESPACE_CONSTRAIN_DISPLAY_APIS,
+                FLAG_ALWAYS_CONSTRAIN_DISPLAY_APIS, /* defaultValue= */ "");
+        mAlwaysConstrainConfigMap = buildConfigMap(alwaysConstrainConfigStr);
+    }
+
+    /**
+     * Processes the configuration string into a map of version codes, for the given
+     * configuration to be applied to the specified packages. If the given package
+     * entry string is invalid, then the map will not contain an entry for the package.
+     *
+     * @param configStr A configuration string expected to be in the format of a list of package
+     *                  entries separated by ','. A package entry expected to be in the format
+     *                  '<package-name>:<min-version-code>?:<max-version-code>?'.
+     * @return a map of configuration entries, where each key is a package name. Each value is
+     * a pair of version codes, in the format 'Pair(<min-version-code>, <max-version-code>)'.
+     */
+    private static ArrayMap<String, Pair<Long, Long>> buildConfigMap(String configStr) {
+        ArrayMap<String, Pair<Long, Long>> configMap = new ArrayMap<>();
+        // String#split returns a non-empty array given an empty string.
+        if (configStr.isEmpty()) {
+            return configMap;
+        }
+        for (String packageEntryString : configStr.split(",")) {
+            List<String> packageAndVersions = Arrays.asList(packageEntryString.split(":", 3));
+            if (packageAndVersions.size() != 3) {
+                Slog.w(TAG, "Invalid package entry in flag 'never/always_constrain_display_apis': "
+                        + packageEntryString);
+                // Skip this entry.
+                continue;
+            }
+            String packageName = packageAndVersions.get(0);
+            String minVersionCodeStr = packageAndVersions.get(1);
+            String maxVersionCodeStr = packageAndVersions.get(2);
+            try {
+                final long minVersion =
+                        minVersionCodeStr.isEmpty() ? Long.MIN_VALUE : Long.parseLong(
+                                minVersionCodeStr);
+                final long maxVersion =
+                        maxVersionCodeStr.isEmpty() ? Long.MAX_VALUE : Long.parseLong(
+                                maxVersionCodeStr);
+                Pair<Long, Long> minMaxVersionCodes = new Pair<>(minVersion, maxVersion);
+                configMap.put(packageName, minMaxVersionCodes);
+            } catch (NumberFormatException e) {
+                Slog.w(TAG, "Invalid APK version code in package entry: " + packageEntryString);
+                // Skip this entry.
+            }
+        }
+        return configMap;
     }
 
     /**
      * Returns true if the flag with the given {@code flagName} contains a package entry that
      * matches the given {@code applicationInfo}.
      *
+     * @param configMap the map representing the current configuration value to examine
      * @param applicationInfo Information about the application/package.
      */
-    private static boolean flagHasMatchingPackageEntry(String flagName,
+    private static boolean flagHasMatchingPackageEntry(Map<String, Pair<Long, Long>> configMap,
             ApplicationInfo applicationInfo) {
-        String configStr = DeviceConfig.getString(NAMESPACE_CONSTRAIN_DISPLAY_APIS,
-                flagName, /* defaultValue= */ "");
-
-        // String#split returns a non-empty array given an empty string.
-        if (configStr.isEmpty()) {
+        if (configMap.isEmpty()) {
             return false;
         }
-
-        for (String packageEntryString : configStr.split(",")) {
-            if (matchesApplicationInfo(packageEntryString, applicationInfo)) {
-                return true;
-            }
+        if (!configMap.containsKey(applicationInfo.packageName)) {
+            return false;
         }
-
-        return false;
+        return matchesApplicationInfo(configMap.get(applicationInfo.packageName), applicationInfo);
     }
 
     /**
-     * Parses the given {@code packageEntryString} and returns true if {@code
-     * applicationInfo.packageName} matches the package name in the config and {@code
-     * applicationInfo.longVersionCode} is within the version range in the config.
+     * Parses the given {@code minMaxVersionCodes} and returns true if {@code
+     * applicationInfo.longVersionCode} is within the version range in the pair.
+     * Returns false otherwise.
      *
-     * <p>Logs a warning and returns false in case the given {@code packageEntryString} is invalid.
-     *
-     * @param packageEntryStr A package entry expected to be in the format
-     *                        '<package-name>:<min-version-code>?:<max-version-code>?'.
+     * @param minMaxVersionCodes A pair expected to be in the format
+     *                        'Pair(<min-version-code>, <max-version-code>)'.
      * @param applicationInfo Information about the application/package.
      */
-    private static boolean matchesApplicationInfo(String packageEntryStr,
+    private static boolean matchesApplicationInfo(Pair<Long, Long> minMaxVersionCodes,
             ApplicationInfo applicationInfo) {
-        List<String> packageAndVersions = Arrays.asList(packageEntryStr.split(":", 3));
-        if (packageAndVersions.size() != 3) {
-            Slog.w(TAG, "Invalid package entry in flag 'never_constrain_display_apis': "
-                    + packageEntryStr);
-            return false;
-        }
-        String packageName = packageAndVersions.get(0);
-        String minVersionCodeStr = packageAndVersions.get(1);
-        String maxVersionCodeStr = packageAndVersions.get(2);
-
-        if (!packageName.equals(applicationInfo.packageName)) {
-            return false;
-        }
-        long version = applicationInfo.longVersionCode;
-        try {
-            if (!minVersionCodeStr.isEmpty() && version < Long.parseLong(minVersionCodeStr)) {
-                return false;
-            }
-            if (!maxVersionCodeStr.isEmpty() && version > Long.parseLong(maxVersionCodeStr)) {
-                return false;
-            }
-        } catch (NumberFormatException e) {
-            Slog.w(TAG, "Invalid APK version code in package entry: " + packageEntryStr);
-            return false;
-        }
-        return true;
+        return applicationInfo.longVersionCode >= minMaxVersionCodes.first
+                && applicationInfo.longVersionCode <= minMaxVersionCodes.second;
     }
 }
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 43ef33e..28046c5 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -151,6 +151,12 @@
     int BIOMETRIC_ERROR_RE_ENROLL = 16;
 
     /**
+     * The privacy setting has been enabled and will block use of the sensor.
+     * @hide
+     */
+    int BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED = 18;
+
+    /**
      * This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
      * because the authentication attempt was unsuccessful.
      * @hide
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index fe43c83..fd46f24 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -69,7 +69,7 @@
             BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
             BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
             BIOMETRIC_ERROR_RE_ENROLL,
-            FACE_ERROR_UNKNOWN
+            FACE_ERROR_UNKNOWN,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface FaceError {}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 9f77a7e..0b02a91 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2632,7 +2632,8 @@
      * </tbody>
      * </table>
      * <p>For applications targeting SDK version 31 or newer, if the mobile device declares to be
-     * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS media performance class} S,
+     * media performance class 12 or higher by setting
+     * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS } to be 31 or larger,
      * the primary camera devices (first rear/front camera in the camera ID list) will not
      * support JPEG sizes smaller than 1080p. If the application configures a JPEG stream
      * smaller than 1080p, the camera device will round up the JPEG image size to at least
@@ -2705,9 +2706,11 @@
      * </tbody>
      * </table>
      * <p>For applications targeting SDK version 31 or newer, if the mobile device doesn't declare
-     * to be media performance class S, or if the camera device isn't a primary rear/front
-     * camera, the minimum required output stream configurations are the same as for applications
-     * targeting SDK version older than 31.</p>
+     * to be media performance class 12 or better by setting
+     * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS } to be 31 or larger,
+     * or if the camera device isn't a primary rear/front camera, the minimum required output
+     * stream configurations are the same as for applications targeting SDK version older than
+     * 31.</p>
      * <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} for additional
      * mandatory stream configurations on a per-capability basis.</p>
      * <p>Exception on 176x144 (QCIF) resolution: camera devices usually have a fixed capability for
diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java
index 74b814e..c8b4226e 100644
--- a/core/java/android/os/AppZygote.java
+++ b/core/java/android/os/AppZygote.java
@@ -45,6 +45,8 @@
     // Last UID/GID of the range the AppZygote can setuid()/setgid() to
     private final int mZygoteUidGidMax;
 
+    private final int mZygoteRuntimeFlags;
+
     private final Object mLock = new Object();
 
     /**
@@ -56,11 +58,13 @@
 
     private final ApplicationInfo mAppInfo;
 
-    public AppZygote(ApplicationInfo appInfo, int zygoteUid, int uidGidMin, int uidGidMax) {
+    public AppZygote(ApplicationInfo appInfo, int zygoteUid, int uidGidMin, int uidGidMax,
+            int runtimeFlags) {
         mAppInfo = appInfo;
         mZygoteUid = zygoteUid;
         mZygoteUidGidMin = uidGidMin;
         mZygoteUidGidMax = uidGidMax;
+        mZygoteRuntimeFlags = runtimeFlags;
     }
 
     /**
@@ -110,7 +114,7 @@
                     mZygoteUid,
                     mZygoteUid,
                     null,  // gids
-                    0,  // runtimeFlags
+                    mZygoteRuntimeFlags,  // runtimeFlags
                     "app_zygote",  // seInfo
                     abi,  // abi
                     abi, // acceptedAbiList
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fdd3836..07feb44 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -953,6 +953,22 @@
     public static final String ACTION_LOCKSCREEN_SETTINGS = "android.settings.LOCK_SCREEN_SETTINGS";
 
     /**
+     * Activity Action: Show settings to allow pairing bluetooth devices.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_BLUETOOTH_PAIRING_SETTINGS =
+            "android.settings.BLUETOOTH_PAIRING_SETTINGS";
+
+    /**
      * Activity Action: Show settings to configure input methods, in particular
      * allowing the user to enable input methods.
      * <p>
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 4d0fc16..ee32ce4 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1750,8 +1750,9 @@
     /**
      * Called when there has been a failure transferring the {@link AssistStructure} to
      * the assistant.  This may happen, for example, if the data is too large and results
-     * in an out of memory exception, or the client has provided corrupt data.  This will
-     * be called immediately before {@link #onHandleAssist} and the AssistStructure supplied
+     * in an out of memory exception, the data has been cleared during transferring due to
+     * the new incoming assist data, or the client has provided corrupt data. This will be
+     * called immediately before {@link #onHandleAssist} and the AssistStructure supplied
      * there afterwards will be null.
      *
      * @param failure The failure exception that was thrown when building the
@@ -1789,7 +1790,8 @@
      * Called to receive data from the application that the user was currently viewing when
      * an assist session is started. If the original show request did not specify
      * {@link #SHOW_WITH_ASSIST}, {@link AssistState} parameter will only provide
-     * {@link ActivityId}.
+     * {@link ActivityId}. If there was a failure to write the assist data to
+     * {@link AssistStructure}, the {@link AssistState#getAssistStructure()} will return null.
      *
      * <p>This method is called for all activities along with an index and count that indicates
      * which activity the data is for. {@code index} will be between 0 and {@code count}-1 and
@@ -2214,7 +2216,8 @@
          * @return If available, the structure definition of all windows currently
          * displayed by the app. May be null if assist data has been disabled by the user
          * or device policy; will be null if the original show request did not specify
-         * {@link #SHOW_WITH_ASSIST}; will be an empty stub if the application has disabled assist
+         * {@link #SHOW_WITH_ASSIST} or the assist data has been corrupt when writing the data to
+         * {@link AssistStructure}; will be an empty stub if the application has disabled assist
          * by marking its window as secure.
          */
         public @Nullable AssistStructure getAssistStructure() {
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 4edff27..0b50192 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -646,7 +646,7 @@
             e.fillInStackTrace();
             Log.w(TAG, "New hash " + hash
                     + " is before end of array hash " + mHashes[index-1]
-                    + " at index " + index + " key " + key, e);
+                    + " at index " + index + (DEBUG ? " key " + key : ""), e);
             put(key, value);
             return;
         }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 86cf28f..e08b913 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -54,6 +54,10 @@
     /** @hide */
     public static final String SETTINGS_SUPPORT_LARGE_SCREEN = "settings_support_large_screen";
 
+    /** @hide */
+    public static final String SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS =
+            "settings_enable_monitor_phantom_procs";
+
     private static final Map<String, String> DEFAULT_FLAGS;
 
     static {
@@ -76,6 +80,7 @@
         DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
         DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
+        DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
     }
 
     private static final Set<String> PERSISTENT_FLAGS;
@@ -83,6 +88,7 @@
         PERSISTENT_FLAGS = new HashSet<>();
         PERSISTENT_FLAGS.add(SETTINGS_PROVIDER_MODEL);
         PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
+        PERSISTENT_FLAGS.add(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
     }
 
     /**
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 9d1bf17..fb534c7 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -435,7 +435,10 @@
                     if (view.getViewTranslationResponse() != null
                             && view.getViewTranslationResponse().equals(response)) {
                         if (callback instanceof TextViewTranslationCallback) {
-                            if (((TextViewTranslationCallback) callback).isShowingTranslation()) {
+                            TextViewTranslationCallback textViewCallback =
+                                    (TextViewTranslationCallback) callback;
+                            if (textViewCallback.isShowingTranslation()
+                                    || textViewCallback.isAnimationRunning()) {
                                 if (DEBUG) {
                                     Log.d(TAG, "Duplicate ViewTranslationResponse for " + autofillId
                                             + ". Ignoring.");
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 721260e..1244d75 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3682,14 +3682,14 @@
                                     if (!mEdgeGlowBottom.isFinished()) {
                                         mEdgeGlowBottom.onRelease();
                                     }
-                                    invalidateTopGlow();
+                                    invalidateEdgeEffects();
                                 } else if (incrementalDeltaY < 0) {
                                     mEdgeGlowBottom.onPullDistance((float) overscroll / getHeight(),
                                             1.f - (float) x / getWidth());
                                     if (!mEdgeGlowTop.isFinished()) {
                                         mEdgeGlowTop.onRelease();
                                     }
-                                    invalidateBottomGlow();
+                                    invalidateEdgeEffects();
                                 }
                             }
                         }
@@ -3729,7 +3729,7 @@
                             if (!mEdgeGlowBottom.isFinished()) {
                                 mEdgeGlowBottom.onRelease();
                             }
-                            invalidateTopGlow();
+                            invalidateEdgeEffects();
                         } else if (rawDeltaY < 0) {
                             mEdgeGlowBottom.onPullDistance(
                                     (float) -overScrollDistance / getHeight(),
@@ -3737,7 +3737,7 @@
                             if (!mEdgeGlowTop.isFinished()) {
                                 mEdgeGlowTop.onRelease();
                             }
-                            invalidateBottomGlow();
+                            invalidateEdgeEffects();
                         }
                     }
                 }
@@ -3783,17 +3783,21 @@
         // First allow releasing existing overscroll effect:
         float consumed = 0;
         if (mEdgeGlowTop.getDistance() != 0) {
-            consumed = mEdgeGlowTop.onPullDistance((float) deltaY / getHeight(),
-                    (float) x / getWidth());
-            if (consumed != 0f) {
-                invalidateTopGlow();
+            if (canScrollUp()) {
+                mEdgeGlowTop.onRelease();
+            } else {
+                consumed = mEdgeGlowTop.onPullDistance((float) deltaY / getHeight(),
+                        (float) x / getWidth());
             }
+            invalidateEdgeEffects();
         } else if (mEdgeGlowBottom.getDistance() != 0) {
-            consumed = -mEdgeGlowBottom.onPullDistance((float) -deltaY / getHeight(),
-                    1f - (float) x / getWidth());
-            if (consumed != 0f) {
-                invalidateBottomGlow();
+            if (canScrollDown()) {
+                mEdgeGlowBottom.onRelease();
+            } else {
+                consumed = -mEdgeGlowBottom.onPullDistance((float) -deltaY / getHeight(),
+                        1f - (float) x / getWidth());
             }
+            invalidateEdgeEffects();
         }
         int pixelsConsumed = Math.round(consumed * getHeight());
         return deltaY - pixelsConsumed;
@@ -3803,30 +3807,16 @@
      * @return <code>true</code> if either the top or bottom edge glow is currently active or
      * <code>false</code> if it has no value to release.
      */
-    private boolean isGlowActive() {
-        return mEdgeGlowBottom.getDistance() != 0 || mEdgeGlowTop.getDistance() != 0;
+    private boolean doesTouchStopStretch() {
+        return (mEdgeGlowBottom.getDistance() != 0 && !canScrollDown())
+                || (mEdgeGlowTop.getDistance() != 0 && !canScrollUp());
     }
 
-    private void invalidateTopGlow() {
+    private void invalidateEdgeEffects() {
         if (!shouldDisplayEdgeEffects()) {
             return;
         }
-        final boolean clipToPadding = getClipToPadding();
-        final int top = clipToPadding ? mPaddingTop : 0;
-        final int left = clipToPadding ? mPaddingLeft : 0;
-        final int right = clipToPadding ? getWidth() - mPaddingRight : getWidth();
-        invalidate(left, top, right, top + mEdgeGlowTop.getMaxHeight());
-    }
-
-    private void invalidateBottomGlow() {
-        if (!shouldDisplayEdgeEffects()) {
-            return;
-        }
-        final boolean clipToPadding = getClipToPadding();
-        final int bottom = clipToPadding ? getHeight() - mPaddingBottom : getHeight();
-        final int left = clipToPadding ? mPaddingLeft : 0;
-        final int right = clipToPadding ? getWidth() - mPaddingRight : getWidth();
-        invalidate(left, bottom - mEdgeGlowBottom.getMaxHeight(), right, bottom);
+        invalidate();
     }
 
     @Override
@@ -4469,7 +4459,7 @@
                 final int edgeY = Math.min(0, scrollY + mFirstPositionDistanceGuess) + translateY;
                 canvas.translate(translateX, edgeY);
                 if (mEdgeGlowTop.draw(canvas)) {
-                    invalidateTopGlow();
+                    invalidateEdgeEffects();
                 }
                 canvas.restoreToCount(restoreCount);
             }
@@ -4483,7 +4473,7 @@
                 canvas.translate(edgeX, edgeY);
                 canvas.rotate(180, width, 0);
                 if (mEdgeGlowBottom.draw(canvas)) {
-                    invalidateBottomGlow();
+                    invalidateEdgeEffects();
                 }
                 canvas.restoreToCount(restoreCount);
             }
@@ -4573,7 +4563,7 @@
                 mActivePointerId = ev.getPointerId(0);
 
                 int motionPosition = findMotionRow(y);
-                if (isGlowActive()) {
+                if (doesTouchStopStretch()) {
                     // Pressed during edge effect, so this is considered the same as a fling catch.
                     touchMode = mTouchMode = TOUCH_MODE_FLING;
                 } else if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) {
@@ -6579,7 +6569,7 @@
      */
     public void setBottomEdgeEffectColor(@ColorInt int color) {
         mEdgeGlowBottom.setColor(color);
-        invalidateBottomGlow();
+        invalidateEdgeEffects();
     }
 
     /**
@@ -6593,7 +6583,7 @@
      */
     public void setTopEdgeEffectColor(@ColorInt int color) {
         mEdgeGlowTop.setColor(color);
-        invalidateTopGlow();
+        invalidateEdgeEffects();
     }
 
     /**
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 3d4d9ec..dfd853a 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -3696,18 +3696,21 @@
     }
 
     private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) {
+        if (hierarchyRoot == null) {
+            mBitmapCache = src.mBitmapCache;
+            mApplicationInfoCache = src.mApplicationInfoCache;
+        } else {
+            mBitmapCache = hierarchyRoot.mBitmapCache;
+            mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
+        }
         if (hierarchyRoot == null || src.mIsRoot) {
             // If there's no provided root, or if src was itself a root, then this RemoteViews is
             // the root of the new hierarchy.
             mIsRoot = true;
-            mBitmapCache = new BitmapCache();
-            mApplicationInfoCache = new ApplicationInfoCache();
             hierarchyRoot = this;
         } else {
             // Otherwise, we're a descendant in the hierarchy.
             mIsRoot = false;
-            mBitmapCache = hierarchyRoot.mBitmapCache;
-            mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
         }
         mApplication = src.mApplication;
         mLayoutId = src.mLayoutId;
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
index 4a78f3e..942be21 100644
--- a/core/java/android/widget/TextViewTranslationCallback.java
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -46,6 +46,7 @@
 
     private TranslationTransformationMethod mTranslationTransformation;
     private boolean mIsShowingTranslation = false;
+    private boolean mAnimationRunning = false;
     private boolean mIsTextPaddingEnabled = false;
     private CharSequence mPaddedText;
     private int mAnimationDurationMillis = 250; // default value
@@ -92,6 +93,7 @@
                 (TextView) view,
                 () -> {
                     mIsShowingTranslation = true;
+                    mAnimationRunning = false;
                     // TODO(b/178353965): well-handle setTransformationMethod.
                     ((TextView) view).setTransformationMethod(transformation);
                 });
@@ -124,6 +126,7 @@
                     (TextView) view,
                     () -> {
                         mIsShowingTranslation = false;
+                        mAnimationRunning = false;
                         ((TextView) view).setTransformationMethod(transformation);
                     });
             if (!TextUtils.isEmpty(mContentDescription)) {
@@ -162,6 +165,13 @@
         return mIsShowingTranslation;
     }
 
+    /**
+     * Returns whether the view is running animation to show or hide the translation.
+     */
+    public boolean isAnimationRunning() {
+        return mAnimationRunning;
+    }
+
     @Override
     public void enableContentPadding() {
         mIsTextPaddingEnabled = true;
@@ -230,6 +240,7 @@
             mAnimator.end();
             // Note: mAnimator is now null; do not use again here.
         }
+        mAnimationRunning = true;
         int fadedOutColor = colorWithAlpha(view.getCurrentTextColor(), 0);
         mAnimator = ValueAnimator.ofArgb(view.getCurrentTextColor(), fadedOutColor);
         mAnimator.addUpdateListener(
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index 1ad0452..4399207 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -44,4 +44,10 @@
      * Unregisters remote animations per transition type for the organizer.
      */
     void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer);
+
+    /**
+      * Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
+      * only occupies a portion of Task bounds.
+      */
+    boolean isActivityEmbedded(in IBinder activityToken);
 }
diff --git a/core/java/android/window/PictureInPictureSurfaceTransaction.java b/core/java/android/window/PictureInPictureSurfaceTransaction.java
index dbf7eb3..2bf2f319 100644
--- a/core/java/android/window/PictureInPictureSurfaceTransaction.java
+++ b/core/java/android/window/PictureInPictureSurfaceTransaction.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Matrix;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -34,9 +35,10 @@
  * @hide
  */
 public final class PictureInPictureSurfaceTransaction implements Parcelable {
+    private static final float NOT_SET = -1f;
 
-    public final float mPositionX;
-    public final float mPositionY;
+    public final float mAlpha;
+    public final PointF mPosition;
 
     public final float[] mFloat9;
 
@@ -45,33 +47,37 @@
 
     public final float mCornerRadius;
 
-    private final Rect mWindowCrop = new Rect();
+    private final Rect mWindowCrop;
 
-    public PictureInPictureSurfaceTransaction(Parcel in) {
-        mPositionX = in.readFloat();
-        mPositionY = in.readFloat();
+    private PictureInPictureSurfaceTransaction(Parcel in) {
+        mAlpha = in.readFloat();
+        mPosition = in.readTypedObject(PointF.CREATOR);
         mFloat9 = new float[9];
         in.readFloatArray(mFloat9);
         mRotation = in.readFloat();
         mCornerRadius = in.readFloat();
-        mWindowCrop.set(Objects.requireNonNull(in.readTypedObject(Rect.CREATOR)));
+        mWindowCrop = in.readTypedObject(Rect.CREATOR);
     }
 
-    public PictureInPictureSurfaceTransaction(float positionX, float positionY,
-            float[] float9, float rotation, float cornerRadius,
+    private PictureInPictureSurfaceTransaction(float alpha, @Nullable PointF position,
+            @Nullable float[] float9, float rotation, float cornerRadius,
             @Nullable Rect windowCrop) {
-        mPositionX = positionX;
-        mPositionY = positionY;
-        mFloat9 = Arrays.copyOf(float9, 9);
-        mRotation = rotation;
-        mCornerRadius = cornerRadius;
-        if (windowCrop != null) {
-            mWindowCrop.set(windowCrop);
+        mAlpha = alpha;
+        mPosition = position;
+        if (float9 == null) {
+            mFloat9 = new float[9];
+            Matrix.IDENTITY_MATRIX.getValues(mFloat9);
+            mRotation = 0;
+        } else {
+            mFloat9 = Arrays.copyOf(float9, 9);
+            mRotation = rotation;
         }
+        mCornerRadius = cornerRadius;
+        mWindowCrop = (windowCrop == null) ? null : new Rect(windowCrop);
     }
 
     public PictureInPictureSurfaceTransaction(PictureInPictureSurfaceTransaction other) {
-        this(other.mPositionX, other.mPositionY,
+        this(other.mAlpha, other.mPosition,
                 other.mFloat9, other.mRotation, other.mCornerRadius, other.mWindowCrop);
     }
 
@@ -82,13 +88,18 @@
         return matrix;
     }
 
+    /** @return {@code true} if this transaction contains setting corner radius. */
+    public boolean hasCornerRadiusSet() {
+        return mCornerRadius > 0;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (!(o instanceof PictureInPictureSurfaceTransaction)) return false;
         PictureInPictureSurfaceTransaction that = (PictureInPictureSurfaceTransaction) o;
-        return Objects.equals(mPositionX, that.mPositionX)
-                && Objects.equals(mPositionY, that.mPositionY)
+        return Objects.equals(mAlpha, that.mAlpha)
+                && Objects.equals(mPosition, that.mPosition)
                 && Arrays.equals(mFloat9, that.mFloat9)
                 && Objects.equals(mRotation, that.mRotation)
                 && Objects.equals(mCornerRadius, that.mCornerRadius)
@@ -97,7 +108,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mPositionX, mPositionY, Arrays.hashCode(mFloat9),
+        return Objects.hash(mAlpha, mPosition, Arrays.hashCode(mFloat9),
                 mRotation, mCornerRadius, mWindowCrop);
     }
 
@@ -108,8 +119,8 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeFloat(mPositionX);
-        out.writeFloat(mPositionY);
+        out.writeFloat(mAlpha);
+        out.writeTypedObject(mPosition, 0 /* flags */);
         out.writeFloatArray(mFloat9);
         out.writeFloat(mRotation);
         out.writeFloat(mCornerRadius);
@@ -120,8 +131,8 @@
     public String toString() {
         final Matrix matrix = getMatrix();
         return "PictureInPictureSurfaceTransaction("
-                + " posX=" + mPositionX
-                + " posY=" + mPositionY
+                + " alpha=" + mAlpha
+                + " position=" + mPosition
                 + " matrix=" + matrix.toShortString()
                 + " rotation=" + mRotation
                 + " cornerRadius=" + mCornerRadius
@@ -134,11 +145,20 @@
             @NonNull SurfaceControl surfaceControl,
             @NonNull SurfaceControl.Transaction tx) {
         final Matrix matrix = surfaceTransaction.getMatrix();
-        tx.setMatrix(surfaceControl, matrix, new float[9])
-                .setPosition(surfaceControl,
-                        surfaceTransaction.mPositionX, surfaceTransaction.mPositionY)
-                .setWindowCrop(surfaceControl, surfaceTransaction.mWindowCrop)
-                .setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius);
+        tx.setMatrix(surfaceControl, matrix, new float[9]);
+        if (surfaceTransaction.mPosition != null) {
+            tx.setPosition(surfaceControl,
+                    surfaceTransaction.mPosition.x, surfaceTransaction.mPosition.y);
+        }
+        if (surfaceTransaction.mWindowCrop != null) {
+            tx.setWindowCrop(surfaceControl, surfaceTransaction.mWindowCrop);
+        }
+        if (surfaceTransaction.hasCornerRadiusSet()) {
+            tx.setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius);
+        }
+        if (surfaceTransaction.mAlpha != NOT_SET) {
+            tx.setAlpha(surfaceControl, surfaceTransaction.mAlpha);
+        }
     }
 
     public static final @android.annotation.NonNull Creator<PictureInPictureSurfaceTransaction>
@@ -151,4 +171,44 @@
                     return new PictureInPictureSurfaceTransaction[size];
                 }
             };
+
+    public static class Builder {
+        private float mAlpha = NOT_SET;
+        private PointF mPosition;
+        private float[] mFloat9;
+        private float mRotation;
+        private float mCornerRadius = NOT_SET;
+        private Rect mWindowCrop;
+
+        public Builder setAlpha(float alpha) {
+            mAlpha = alpha;
+            return this;
+        }
+
+        public Builder setPosition(float x, float y) {
+            mPosition = new PointF(x, y);
+            return this;
+        }
+
+        public Builder setTransform(@NonNull float[] float9, float rotation) {
+            mFloat9 = Arrays.copyOf(float9, 9);
+            mRotation = rotation;
+            return this;
+        }
+
+        public Builder setCornerRadius(float cornerRadius) {
+            mCornerRadius = cornerRadius;
+            return this;
+        }
+
+        public Builder setWindowCrop(@NonNull Rect windowCrop) {
+            mWindowCrop = new Rect(windowCrop);
+            return this;
+        }
+
+        public PictureInPictureSurfaceTransaction build() {
+            return new PictureInPictureSurfaceTransaction(mAlpha, mPosition,
+                    mFloat9, mRotation, mCornerRadius, mWindowCrop);
+        }
+    }
 }
diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java
index b9bf009..090dbff 100644
--- a/core/java/android/window/SplashScreen.java
+++ b/core/java/android/window/SplashScreen.java
@@ -43,6 +43,11 @@
  */
 public interface SplashScreen {
     /**
+     * The splash screen style is not defined.
+     * @hide
+     */
+    int SPLASH_SCREEN_STYLE_UNDEFINED = -1;
+    /**
      * Force splash screen to be empty.
      * @hide
      */
@@ -55,6 +60,7 @@
 
     /** @hide */
     @IntDef(prefix = { "SPLASH_SCREEN_STYLE_" }, value = {
+            SPLASH_SCREEN_STYLE_UNDEFINED,
             SPLASH_SCREEN_STYLE_EMPTY,
             SPLASH_SCREEN_STYLE_ICON
     })
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 7e7d370..9c2fde0 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -216,4 +216,17 @@
             return null;
         }
     }
+
+    /**
+     * Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
+     * only occupies a portion of Task bounds.
+     * @hide
+     */
+    public boolean isActivityEmbedded(@NonNull IBinder activityToken) {
+        try {
+            return getController().isActivityEmbedded(activityToken);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index b331a9e..4ba7ef2 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -15,7 +15,6 @@
  */
 package android.window;
 
-import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
 import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
 import static android.window.ConfigurationHelper.isDifferentDisplay;
 import static android.window.ConfigurationHelper.shouldUpdateResources;
@@ -222,14 +221,7 @@
                         () -> windowContext.dispatchConfigurationChanged(newConfig));
             }
 
-            // Dispatch onConfigurationChanged only if there's a significant public change to
-            // make it compatible with the original behavior.
-            final Configuration[] sizeConfigurations = context.getResources()
-                    .getSizeConfigurations();
-            final SizeConfigurationBuckets buckets = sizeConfigurations != null
-                    ? new SizeConfigurationBuckets(sizeConfigurations) : null;
-            final int diff = diffPublicWithSizeBuckets(mConfiguration, newConfig, buckets);
-
+            final int diff = mConfiguration.diffPublicOnly(newConfig);
             if (shouldReportConfigChange && diff != 0
                     && context instanceof WindowProviderService) {
                 final WindowProviderService windowProviderService = (WindowProviderService) context;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 786af5f..7bb1ed8 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -163,9 +163,6 @@
     private AppPredictor mWorkAppPredictor;
     private boolean mShouldDisplayLandscape;
 
-    private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
-    private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
-
     @UnsupportedAppUsage
     public ChooserActivity() {
     }
@@ -275,6 +272,7 @@
 
     private int mCurrAvailableWidth = 0;
     private int mLastNumberOfChildren = -1;
+    private int mMaxTargetsPerRow = 1;
 
     private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment";
 
@@ -741,8 +739,9 @@
             mCallerChooserTargets = targets;
         }
 
-        mShouldDisplayLandscape = shouldDisplayLandscape(
-                getResources().getConfiguration().orientation);
+        mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row);
+        mShouldDisplayLandscape =
+                shouldDisplayLandscape(getResources().getConfiguration().orientation);
         setRetainInOnStop(intent.getBooleanExtra(EXTRA_PRIVATE_RETAIN_IN_ON_STOP, false));
         super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
                 null, false);
@@ -916,7 +915,7 @@
                 adapter,
                 getPersonalProfileUserHandle(),
                 /* workProfileUserHandle= */ null,
-                isSendAction(getTargetIntent()), getMaxTargetsPerRow());
+                isSendAction(getTargetIntent()), mMaxTargetsPerRow);
     }
 
     private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles(
@@ -945,7 +944,7 @@
                 selectedProfile,
                 getPersonalProfileUserHandle(),
                 getWorkProfileUserHandle(),
-                isSendAction(getTargetIntent()), getMaxTargetsPerRow());
+                isSendAction(getTargetIntent()), mMaxTargetsPerRow);
     }
 
     private int findSelectedProfile() {
@@ -1107,6 +1106,7 @@
         }
 
         mShouldDisplayLandscape = shouldDisplayLandscape(newConfig.orientation);
+        mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row);
         adjustPreviewWidth(newConfig.orientation, null);
         updateStickyContentPreview();
     }
@@ -2690,7 +2690,7 @@
                 // and b/150936654
                 recyclerView.setAdapter(gridAdapter);
                 ((GridLayoutManager) recyclerView.getLayoutManager()).setSpanCount(
-                        getMaxTargetsPerRow());
+                        mMaxTargetsPerRow);
             }
 
             UserHandle currentUserHandle = mChooserMultiProfilePagerAdapter.getCurrentUserHandle();
@@ -2855,7 +2855,7 @@
 
     @Override // ChooserListCommunicator
     public int getMaxRankedTargets() {
-        return getMaxTargetsPerRow();
+        return mMaxTargetsPerRow;
     }
 
     @Override // ChooserListCommunicator
@@ -3203,13 +3203,6 @@
         }
     }
 
-    int getMaxTargetsPerRow() {
-        int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT;
-        if (mShouldDisplayLandscape) {
-            maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE;
-        }
-        return maxTargets;
-    }
     /**
      * Adapter for all types of items and targets in ShareSheet.
      * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
@@ -3277,7 +3270,11 @@
                 return false;
             }
 
-            int newWidth = width / getMaxTargetsPerRow();
+            // Limit width to the maximum width of the chooser activity
+            int maxWidth = getResources().getDimensionPixelSize(R.dimen.chooser_width);
+            width = Math.min(maxWidth, width);
+
+            int newWidth = width / mMaxTargetsPerRow;
             if (newWidth != mChooserTargetWidth) {
                 mChooserTargetWidth = newWidth;
                 return true;
@@ -3312,7 +3309,7 @@
                             + getAzLabelRowCount()
                             + Math.ceil(
                             (float) mChooserListAdapter.getAlphaTargetCount()
-                                    / getMaxTargetsPerRow())
+                                    / mMaxTargetsPerRow)
             );
         }
 
@@ -3352,7 +3349,7 @@
         public int getCallerAndRankedTargetRowCount() {
             return (int) Math.ceil(
                     ((float) mChooserListAdapter.getCallerTargetCount()
-                            + mChooserListAdapter.getRankedTargetCount()) / getMaxTargetsPerRow());
+                            + mChooserListAdapter.getRankedTargetCount()) / mMaxTargetsPerRow);
         }
 
         // There can be at most one row in the listview, that is internally
@@ -3551,7 +3548,7 @@
                 parentGroup.addView(row2);
 
                 mDirectShareViewHolder = new DirectShareViewHolder(parentGroup,
-                        Lists.newArrayList(row1, row2), getMaxTargetsPerRow(), viewType);
+                        Lists.newArrayList(row1, row2), mMaxTargetsPerRow, viewType);
                 loadViewsIntoGroup(mDirectShareViewHolder);
 
                 return mDirectShareViewHolder;
@@ -3559,7 +3556,7 @@
                 ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent,
                         false);
                 ItemGroupViewHolder holder =
-                        new SingleRowViewHolder(row, getMaxTargetsPerRow(), viewType);
+                        new SingleRowViewHolder(row, mMaxTargetsPerRow, viewType);
                 loadViewsIntoGroup(holder);
 
                 return holder;
@@ -3651,7 +3648,7 @@
             final int serviceCount = mChooserListAdapter.getServiceTargetCount();
             final int serviceRows = (int) Math.ceil((float) serviceCount / getMaxRankedTargets());
             if (position < serviceRows) {
-                return position * getMaxTargetsPerRow();
+                return position * mMaxTargetsPerRow;
             }
 
             position -= serviceRows;
@@ -3660,7 +3657,7 @@
                                                  + mChooserListAdapter.getRankedTargetCount();
             final int callerAndRankedRows = getCallerAndRankedTargetRowCount();
             if (position < callerAndRankedRows) {
-                return serviceCount + position * getMaxTargetsPerRow();
+                return serviceCount + position * mMaxTargetsPerRow;
             }
 
             position -= getAzLabelRowCount() + callerAndRankedRows;
@@ -3673,7 +3670,7 @@
             if (mDirectShareViewHolder != null && canExpandDirectShare) {
                 mDirectShareViewHolder.handleScroll(
                         mChooserMultiProfilePagerAdapter.getActiveAdapterView(), y, oldy,
-                        getMaxTargetsPerRow());
+                        mMaxTargetsPerRow);
             }
         }
 
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c750ead..6faa046 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -366,6 +366,7 @@
     optional bool pip_auto_enter_enabled = 31;
     optional bool in_size_compat_mode = 32;
     optional float min_aspect_ratio = 33;
+    optional bool provides_max_bounds = 34;
 }
 
 /* represents WindowToken */
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 90caacc..933b4d2 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -20,8 +20,10 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:layout_gravity="center"
     android:maxCollapsedHeight="0dp"
     android:maxCollapsedHeightSmall="56dp"
+    android:maxWidth="@dimen/chooser_width"
     android:id="@id/contentPanel">
 
     <RelativeLayout
diff --git a/core/res/res/layout/chooser_grid_preview_image.xml b/core/res/res/layout/chooser_grid_preview_image.xml
index 0d04d7f3..52692b0 100644
--- a/core/res/res/layout/chooser_grid_preview_image.xml
+++ b/core/res/res/layout/chooser_grid_preview_image.xml
@@ -34,7 +34,7 @@
     <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
           android:id="@+id/content_preview_image_1_large"
           android:layout_width="120dp"
-          android:layout_height="140dp"
+          android:layout_height="104dp"
           android:layout_alignParentTop="true"
           android:adjustViewBounds="true"
           android:gravity="center"
@@ -44,7 +44,7 @@
           android:id="@+id/content_preview_image_2_large"
           android:visibility="gone"
           android:layout_width="120dp"
-          android:layout_height="140dp"
+          android:layout_height="104dp"
           android:layout_alignParentTop="true"
           android:layout_toRightOf="@id/content_preview_image_1_large"
           android:layout_marginLeft="10dp"
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index 861e329..624581a 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -56,5 +56,7 @@
          to be aligned to one side of the screen when in landscape mode. -->
     <bool name="config_enableDynamicKeyguardPositioning">true</bool>
 
+    <integer name="config_chooser_max_targets_per_row">6</integer>
+
 </resources>
 
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 02ed848..e8f15fd 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -110,4 +110,7 @@
     <dimen name="immersive_mode_cling_width">380dp</dimen>
 
     <dimen name="floating_toolbar_preferred_width">544dp</dimen>
+
+    <dimen name="chooser_width">624dp</dimen>
+
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4d85164..1c31b1b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5284,4 +5284,6 @@
     <string name="config_work_badge_path_24" translatable="false">
         M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2z
     </string>
+
+    <integer name="config_chooser_max_targets_per_row">4</integer>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 0706d8a..f331f1a 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -900,7 +900,8 @@
     <dimen name="seekbar_thumb_exclusion_max_size">48dp</dimen>
 
     <!-- chooser/resolver (sharesheet) spacing -->
-    <dimen name="chooser_corner_radius">16dp</dimen>
+    <dimen name="chooser_width">412dp</dimen>
+    <dimen name="chooser_corner_radius">28dp</dimen>
     <dimen name="chooser_row_text_option_translate">25dp</dimen>
     <dimen name="chooser_view_spacing">18dp</dimen>
     <dimen name="chooser_edge_margin_thin">16dp</dimen>
@@ -917,7 +918,7 @@
     <dimen name="resolver_icon_size">32dp</dimen>
     <dimen name="resolver_button_bar_spacing">0dp</dimen>
     <dimen name="resolver_badge_size">18dp</dimen>
-    <dimen name="resolver_icon_margin">16dp</dimen>
+    <dimen name="resolver_icon_margin">8dp</dimen>
     <dimen name="resolver_small_margin">18dp</dimen>
     <dimen name="resolver_edge_margin">24dp</dimen>
     <dimen name="resolver_elevation">1dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3c7f026..166d6ab 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1666,6 +1666,8 @@
     <string name="face_setup_notification_title">Set up Face Unlock</string>
     <!-- Contents of a notification that directs the user to set up face unlock by enrolling their face. [CHAR LIMIT=NONE] -->
     <string name="face_setup_notification_content">Unlock your phone by looking at it</string>
+    <!-- Error message indicating that the camera privacy sensor has been turned on [CHAR LIMIT=NONE] -->
+    <string name="face_sensor_privacy_enabled">To use Face Unlock, turn on <b>Camera access</b> in Settings > Privacy</string>
     <!-- Title of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
     <string name="fingerprint_setup_notification_title">Set up more ways to unlock</string>
     <!-- Contents of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a23816e..b017a30 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -275,6 +275,7 @@
   <java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
   <java-symbol type="bool" name="config_avoidGfxAccel" />
   <java-symbol type="bool" name="config_bluetooth_address_validation" />
+  <java-symbol type="integer" name="config_chooser_max_targets_per_row" />
   <java-symbol type="bool" name="config_flipToScreenOffEnabled" />
   <java-symbol type="integer" name="config_flipToScreenOffMaxLatencyMicros" />
   <java-symbol type="bool" name="config_bluetooth_sco_off_call" />
@@ -2570,6 +2571,7 @@
   <java-symbol type="string" name="face_recalibrate_notification_name" />
   <java-symbol type="string" name="face_recalibrate_notification_title" />
   <java-symbol type="string" name="face_recalibrate_notification_content" />
+  <java-symbol type="string" name="face_sensor_privacy_enabled" />
   <java-symbol type="string" name="face_error_unable_to_process" />
   <java-symbol type="string" name="face_error_hw_not_available" />
   <java-symbol type="string" name="face_error_no_space" />
@@ -2843,6 +2845,7 @@
   <java-symbol type="layout" name="date_picker_month_item_material" />
   <java-symbol type="id" name="month_view" />
   <java-symbol type="integer" name="config_zen_repeat_callers_threshold" />
+  <java-symbol type="dimen" name="chooser_width" />
   <java-symbol type="dimen" name="chooser_corner_radius" />
   <java-symbol type="string" name="chooser_no_direct_share_targets" />
   <java-symbol type="drawable" name="chooser_row_layer_list" />
diff --git a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
index 0456029..98485c0 100644
--- a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
+++ b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
@@ -18,8 +18,7 @@
 
 import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
 
 import android.annotation.Nullable;
 import android.platform.test.annotations.Presubmit;
@@ -146,24 +145,17 @@
 
     private static void testNeverConstrainDisplayApis(String packageName, long version,
             boolean expected) {
-        boolean result = ConstrainDisplayApisConfig.neverConstrainDisplayApis(
-                buildApplicationInfo(packageName, version));
-        if (expected) {
-            assertTrue(result);
-        } else {
-            assertFalse(result);
-        }
+        ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig();
+        assertEquals(expected,
+                config.getNeverConstrainDisplayApis(buildApplicationInfo(packageName, version)));
     }
 
     private static void testAlwaysConstrainDisplayApis(String packageName, long version,
             boolean expected) {
-        boolean result = ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(
-                buildApplicationInfo(packageName, version));
-        if (expected) {
-            assertTrue(result);
-        } else {
-            assertFalse(result);
-        }
+        ConstrainDisplayApisConfig config = new ConstrainDisplayApisConfig();
+
+        assertEquals(expected,
+                config.getAlwaysConstrainDisplayApis(buildApplicationInfo(packageName, version)));
     }
 
     private static ApplicationInfo buildApplicationInfo(String packageName, long version) {
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 756425e..0b8dc3f 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -180,6 +180,7 @@
     <assign-permission name="android.permission.WATCH_APPOPS" uid="cameraserver" />
     <assign-permission name="android.permission.MANAGE_APP_OPS_MODES" uid="cameraserver" />
     <assign-permission name="android.permission.OBSERVE_SENSOR_PRIVACY" uid="cameraserver" />
+    <assign-permission name="android.permission.REAL_GET_TASKS" uid="cameraserver" />
 
     <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
 
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index fe6c7ba..b8e8b01 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -91,11 +91,13 @@
      */
     public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
             @Nullable Bundle options, @NonNull SplitRule sideRule,
-            @NonNull Consumer<Exception> failureCallback) {
+            @Nullable Consumer<Exception> failureCallback) {
         try {
             mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule);
         } catch (Exception e) {
-            failureCallback.accept(e);
+            if (failureCallback != null) {
+                failureCallback.accept(e);
+            }
         }
     }
 
@@ -293,11 +295,12 @@
             @NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity,
             @NonNull TaskFragmentContainer secondaryContainer,
             @NonNull SplitRule splitRule) {
+        SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
+                secondaryContainer, splitRule);
+        // Remove container later to prevent pinning escaping toast showing in lock task mode.
         if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) {
             removeExistingSecondaryContainers(wct, primaryContainer);
         }
-        SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
-                secondaryContainer, splitRule);
         mSplitContainers.add(splitContainer);
     }
 
@@ -858,4 +861,12 @@
                     launchingContainer.getTaskFragmentToken());
         }
     }
+
+    /**
+     * Checks if an activity is embedded and its presentation is customized by a
+     * {@link android.window.TaskFragmentOrganizer} to only occupy a portion of Task bounds.
+     */
+    public boolean isActivityEmbedded(@NonNull Activity activity) {
+        return mPresenter.isActivityEmbedded(activity.getActivityToken());
+    }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 81be21c..ade5731 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -112,8 +112,7 @@
         secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
 
         // Set adjacent to each other so that the containers below will be invisible.
-        setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
-                secondaryContainer.getTaskFragmentToken(), rule);
+        setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
 
         mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
 
@@ -149,8 +148,7 @@
                 secondaryActivity, secondaryRectBounds, primaryContainer);
 
         // Set adjacent to each other so that the containers below will be invisible.
-        setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
-                secondaryContainer.getTaskFragmentToken(), rule);
+        setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
 
         mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
 
@@ -269,8 +267,22 @@
         final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
         resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds);
 
-        setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
-                secondaryContainer.getTaskFragmentToken(), rule);
+        setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
+    }
+
+    private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
+            @NonNull TaskFragmentContainer primaryContainer,
+            @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule) {
+        final Rect parentBounds = getParentContainerBounds(primaryContainer);
+        // Clear adjacent TaskFragments if the container is shown in fullscreen, or the
+        // secondaryContainer could not be finished.
+        if (!shouldShowSideBySide(parentBounds, splitRule)) {
+            setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
+                    null /* secondary */, null /* splitRule */);
+        } else {
+            setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
+                    secondaryContainer.getTaskFragmentToken(), splitRule);
+        }
     }
 
     /**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
index 8c8ef92..46bdf6d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -16,6 +16,7 @@
 
 package androidx.window.extensions.embedding;
 
+import static android.os.Process.THREAD_PRIORITY_DISPLAY;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
@@ -29,7 +30,7 @@
 import android.animation.ValueAnimator;
 import android.graphics.Rect;
 import android.os.Handler;
-import android.os.Looper;
+import android.os.HandlerThread;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.IRemoteAnimationFinishedCallback;
@@ -50,10 +51,14 @@
 class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
 
     private static final String TAG = "TaskFragAnimationRunner";
-    private final Handler mHandler = new Handler(Looper.myLooper());
+    private final Handler mHandler;
     private final TaskFragmentAnimationSpec mAnimationSpec;
 
     TaskFragmentAnimationRunner() {
+        HandlerThread animationThread = new HandlerThread(
+                "androidx.window.extensions.embedding", THREAD_PRIORITY_DISPLAY);
+        animationThread.start();
+        mHandler = animationThread.getThreadHandler();
         mAnimationSpec = new TaskFragmentAnimationSpec(mHandler);
     }
 
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index a1a53bc..4d2d055 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -103,7 +103,7 @@
         ActivityThread activityThread = ActivityThread.currentActivityThread();
         for (IBinder token : mInfo.getActivities()) {
             Activity activity = activityThread.getActivity(token);
-            if (activity != null && !allActivities.contains(activity)) {
+            if (activity != null && !activity.isFinishing() && !allActivities.contains(activity)) {
                 allActivities.add(activity);
             }
         }
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index d6678bf..f54ab08 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml
similarity index 85%
rename from libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
rename to libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml
index 22cd384..26848b1 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
+++ b/libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml
@@ -16,6 +16,6 @@
   -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="@color/size_compat_background"/>
-    <corners android:radius="@dimen/size_compat_hint_corner_radius"/>
+    <solid android:color="@color/compat_controls_background"/>
+    <corners android:radius="@dimen/compat_hint_corner_radius"/>
 </shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml b/libs/WindowManager/Shell/res/drawable/compat_hint_point.xml
similarity index 88%
rename from libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
rename to libs/WindowManager/Shell/res/drawable/compat_hint_point.xml
index af9063a..0e0ca37 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
+++ b/libs/WindowManager/Shell/res/drawable/compat_hint_point.xml
@@ -15,11 +15,11 @@
   ~ limitations under the License.
   -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="@dimen/size_compat_hint_point_width"
+        android:width="@dimen/compat_hint_point_width"
         android:height="8dp"
         android:viewportWidth="10"
         android:viewportHeight="8">
     <path
-        android:fillColor="@color/size_compat_background"
+        android:fillColor="@color/compat_controls_background"
         android:pathData="M10,0 l-4.1875,6.6875 a1,1 0 0,1 -1.625,0 l-4.1875,-6.6875z"/>
 </vector>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
index 18caa35..ab74e43 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
@@ -20,16 +20,16 @@
         android:viewportWidth="48"
         android:viewportHeight="48">
     <path
-        android:fillColor="@color/size_compat_background"
+        android:fillColor="@color/compat_controls_background"
         android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0" />
     <group
         android:translateX="12"
         android:translateY="12">
         <path
-            android:fillColor="@color/size_compat_text"
+            android:fillColor="@color/compat_controls_text"
             android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
         <path
-            android:fillColor="@color/size_compat_text"
+            android:fillColor="@color/compat_controls_text"
             android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/>
     </group>
 </vector>
diff --git a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
new file mode 100644
index 0000000..c04e258e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 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:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:clipToPadding="false"
+    android:paddingEnd="@dimen/compat_hint_padding_end"
+    android:paddingBottom="5dp"
+    android:clickable="true">
+
+    <TextView
+        android:id="@+id/compat_mode_hint_text"
+        android:layout_width="188dp"
+        android:layout_height="wrap_content"
+        android:lineSpacingExtra="4sp"
+        android:background="@drawable/compat_hint_bubble"
+        android:padding="16dp"
+        android:textAlignment="viewStart"
+        android:textColor="@color/compat_controls_text"
+        android:textSize="14sp"/>
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end"
+        android:src="@drawable/compat_hint_point"
+        android:paddingHorizontal="@dimen/compat_hint_corner_radius"
+        android:contentDescription="@null"/>
+
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
similarity index 82%
rename from libs/WindowManager/Shell/res/layout/size_compat_ui.xml
rename to libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
index 82ebee2..6f946b2 100644
--- a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
@@ -14,10 +14,15 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.wm.shell.sizecompatui.SizeCompatRestartButton
+<com.android.wm.shell.compatui.CompatUILayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="bottom|end">
+
+    <include android:id="@+id/size_compat_hint"
+         layout="@layout/compat_mode_hint"/>
 
     <FrameLayout
         android:layout_width="@dimen/size_compat_button_width"
@@ -36,4 +41,4 @@
 
     </FrameLayout>
 
-</com.android.wm.shell.sizecompatui.SizeCompatRestartButton>
+</com.android.wm.shell.compatui.CompatUILayout>
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
deleted file mode 100644
index d0e7c42..0000000
--- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.wm.shell.sizecompatui.SizeCompatHintPopup
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
-
-    <FrameLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:clipToPadding="false"
-        android:paddingBottom="5dp">
-
-        <LinearLayout
-            android:id="@+id/size_compat_hint_popup"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:clickable="true">
-
-            <TextView
-                android:layout_width="188dp"
-                android:layout_height="wrap_content"
-                android:lineSpacingExtra="4sp"
-                android:background="@drawable/size_compat_hint_bubble"
-                android:padding="16dp"
-                android:text="@string/restart_button_description"
-                android:textAlignment="viewStart"
-                android:textColor="@color/size_compat_text"
-                android:textSize="14sp"/>
-
-            <ImageView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="end"
-                android:src="@drawable/size_compat_hint_point"
-                android:paddingHorizontal="@dimen/size_compat_hint_corner_radius"
-                android:contentDescription="@null"/>
-
-        </LinearLayout>
-
-    </FrameLayout>
-
-</com.android.wm.shell.sizecompatui.SizeCompatHintPopup>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 23a2172..cf596f7 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -30,9 +30,9 @@
     <color name="bubbles_dark">@color/GM2_grey_800</color>
     <color name="bubbles_icon_tint">@color/GM2_grey_700</color>
 
-    <!-- Size Compat Restart Button -->
-    <color name="size_compat_background">@android:color/system_neutral1_800</color>
-    <color name="size_compat_text">@android:color/system_neutral1_50</color>
+    <!-- Compat controls UI -->
+    <color name="compat_controls_background">@android:color/system_neutral1_800</color>
+    <color name="compat_controls_text">@android:color/system_neutral1_50</color>
 
     <!-- GM2 colors -->
     <color name="GM2_grey_200">#E8EAED</color>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 9e77578..18e91f4 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -206,11 +206,15 @@
     <!-- The height of the size compat restart button including padding. -->
     <dimen name="size_compat_button_height">64dp</dimen>
 
-    <!-- The radius of the corners of the size compat hint bubble. -->
-    <dimen name="size_compat_hint_corner_radius">28dp</dimen>
+    <!-- The radius of the corners of the compat hint bubble. -->
+    <dimen name="compat_hint_corner_radius">28dp</dimen>
 
-    <!-- The width of the size compat hint point. -->
-    <dimen name="size_compat_hint_point_width">10dp</dimen>
+    <!-- The width of the compat hint point. -->
+    <dimen name="compat_hint_point_width">10dp</dimen>
+
+    <!-- The end padding for the compat hint. Computed as (size_compat_button_width / 2
+         - compat_hint_corner_radius - compat_hint_point_width /2). -->
+    <dimen name="compat_hint_padding_end">7dp</dimen>
 
     <!-- The width of the brand image on staring surface. -->
     <dimen name="starting_surface_brand_image_width">200dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 8e98b82..8b3a356 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -52,8 +52,8 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.wm.shell.common.ScreenshotUtils;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.compatui.CompatUIController;
 import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.sizecompatui.SizeCompatUIController;
 import com.android.wm.shell.startingsurface.StartingWindowController;
 
 import java.io.PrintWriter;
@@ -69,7 +69,7 @@
  * TODO(b/167582004): may consider consolidating this class and TaskOrganizer
  */
 public class ShellTaskOrganizer extends TaskOrganizer implements
-        SizeCompatUIController.SizeCompatUICallback {
+        CompatUIController.CompatUICallback {
 
     // Intentionally using negative numbers here so the positive numbers can be used
     // for task id specific listeners that will be added later.
@@ -98,9 +98,9 @@
         default void onTaskInfoChanged(RunningTaskInfo taskInfo) {}
         default void onTaskVanished(RunningTaskInfo taskInfo) {}
         default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {}
-        /** Whether this task listener supports size compat UI. */
-        default boolean supportSizeCompatUI() {
-            // All TaskListeners should support size compat except PIP.
+        /** Whether this task listener supports  compat UI. */
+        default boolean supportCompatUI() {
+            // All TaskListeners should support compat UI except PIP.
             return true;
         }
         /** Attaches the a child window surface to the task surface. */
@@ -159,11 +159,11 @@
     private StartingWindowController mStartingWindow;
 
     /**
-     * In charge of showing size compat UI. Can be {@code null} if device doesn't support size
+     * In charge of showing compat UI. Can be {@code null} if device doesn't support size
      * compat.
      */
     @Nullable
-    private final SizeCompatUIController mSizeCompatUI;
+    private final CompatUIController mCompatUI;
 
     @Nullable
     private final Optional<RecentTasksController> mRecentTasks;
@@ -172,32 +172,32 @@
     private RunningTaskInfo mLastFocusedTaskInfo;
 
     public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
-        this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
+        this(null /* taskOrganizerController */, mainExecutor, context, null /* compatUI */,
                 Optional.empty() /* recentTasksController */);
     }
 
     public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
-            SizeCompatUIController sizeCompatUI) {
-        this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
+            CompatUIController compatUI) {
+        this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
                 Optional.empty() /* recentTasksController */);
     }
 
     public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
-            SizeCompatUIController sizeCompatUI,
+            CompatUIController compatUI,
             Optional<RecentTasksController> recentTasks) {
-        this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
+        this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
                 recentTasks);
     }
 
     @VisibleForTesting
     ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor,
-            Context context, @Nullable SizeCompatUIController sizeCompatUI,
+            Context context, @Nullable CompatUIController compatUI,
             Optional<RecentTasksController> recentTasks) {
         super(taskOrganizerController, mainExecutor);
-        mSizeCompatUI = sizeCompatUI;
+        mCompatUI = compatUI;
         mRecentTasks = recentTasks;
-        if (sizeCompatUI != null) {
-            sizeCompatUI.setSizeCompatUICallback(this);
+        if (compatUI != null) {
+            compatUI.setCompatUICallback(this);
         }
     }
 
@@ -428,7 +428,7 @@
             listener.onTaskAppeared(info.getTaskInfo(), info.getLeash());
         }
         notifyLocusVisibilityIfNeeded(info.getTaskInfo());
-        notifySizeCompatUI(info.getTaskInfo(), listener);
+        notifyCompatUI(info.getTaskInfo(), listener);
     }
 
     /**
@@ -459,8 +459,8 @@
             }
             notifyLocusVisibilityIfNeeded(taskInfo);
             if (updated || !taskInfo.equalsForSizeCompat(data.getTaskInfo())) {
-                // Notify the size compat UI if the listener or task info changed.
-                notifySizeCompatUI(taskInfo, newListener);
+                // Notify the compat UI if the listener or task info changed.
+                notifyCompatUI(taskInfo, newListener);
             }
             if (data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode()) {
                 // Notify the recent tasks when a task changes windowing modes
@@ -504,8 +504,8 @@
                 listener.onTaskVanished(taskInfo);
             }
             notifyLocusVisibilityIfNeeded(taskInfo);
-            // Pass null for listener to remove the size compat UI on this task if there is any.
-            notifySizeCompatUI(taskInfo, null /* taskListener */);
+            // Pass null for listener to remove the compat UI on this task if there is any.
+            notifyCompatUI(taskInfo, null /* taskListener */);
             // Notify the recent tasks that a task has been removed
             mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo));
         }
@@ -618,28 +618,28 @@
     }
 
     /**
-     * Notifies {@link SizeCompatUIController} about the size compat info changed on the give Task
+     * Notifies {@link CompatUIController} about the compat info changed on the give Task
      * to update the UI accordingly.
      *
      * @param taskInfo the new Task info
      * @param taskListener listener to handle the Task Surface placement. {@code null} if task is
      *                     vanished.
      */
-    private void notifySizeCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) {
-        if (mSizeCompatUI == null) {
+    private void notifyCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) {
+        if (mCompatUI == null) {
             return;
         }
 
-        // The task is vanished or doesn't support size compat UI, notify to remove size compat UI
+        // The task is vanished or doesn't support compat UI, notify to remove compat UI
         // on this Task if there is any.
-        if (taskListener == null || !taskListener.supportSizeCompatUI()
+        if (taskListener == null || !taskListener.supportCompatUI()
                 || !taskInfo.topActivityInSizeCompat || !taskInfo.isVisible) {
-            mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
+            mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
                     null /* taskConfig */, null /* taskListener */);
             return;
         }
 
-        mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
+        mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
                 taskInfo.configuration, taskListener);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 519a856..cd635c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -328,6 +328,7 @@
         if (prevBubble == null) {
             // Create a new bubble
             bubble.setSuppressFlyout(suppressFlyout);
+            bubble.markUpdatedAt(mTimeSource.currentTimeMillis());
             doAdd(bubble);
             trim();
         } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
similarity index 86%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
index a703114..99dbfe0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.sizecompatui;
+package com.android.wm.shell.compatui;
 
 import com.android.wm.shell.common.annotations.ExternalThread;
 
 /**
- * Interface to engage size compat UI.
+ * Interface to engage compat UI.
  */
 @ExternalThread
-public interface SizeCompatUI {
+public interface CompatUI {
     /**
-     * Called when the keyguard occluded state changes. Removes all size compat UIs if the
+     * Called when the keyguard occluded state changes. Removes all compat UIs if the
      * keyguard is now occluded.
      * @param occluded indicates if the keyguard is now occluded.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
similarity index 81%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index e06070a..e0b2387 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.sizecompatui;
+package com.android.wm.shell.compatui;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -48,20 +48,20 @@
 
 /**
  * Controls to show/update restart-activity buttons on Tasks based on whether the foreground
- * activities are in size compatibility mode.
+ * activities are in compatibility mode.
  */
-public class SizeCompatUIController implements OnDisplaysChangedListener,
+public class CompatUIController implements OnDisplaysChangedListener,
         DisplayImeController.ImePositionProcessor {
 
     /** Callback for size compat UI interaction. */
-    public interface SizeCompatUICallback {
+    public interface CompatUICallback {
         /** Called when the size compat restart button appears. */
         void onSizeCompatRestartButtonAppeared(int taskId);
         /** Called when the size compat restart button is clicked. */
         void onSizeCompatRestartButtonClicked(int taskId);
     }
 
-    private static final String TAG = "SizeCompatUIController";
+    private static final String TAG = "CompatUIController";
 
     /** Whether the IME is shown on display id. */
     private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1);
@@ -71,7 +71,7 @@
             new SparseArray<>(0);
 
     /** The showing UIs by task id. */
-    private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0);
+    private final SparseArray<CompatUIWindowManager> mActiveLayouts = new SparseArray<>(0);
 
     /** Avoid creating display context frequently for non-default display. */
     private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
@@ -82,17 +82,17 @@
     private final DisplayImeController mImeController;
     private final SyncTransactionQueue mSyncQueue;
     private final ShellExecutor mMainExecutor;
-    private final SizeCompatUIImpl mImpl = new SizeCompatUIImpl();
+    private final CompatUIImpl mImpl = new CompatUIImpl();
 
-    private SizeCompatUICallback mCallback;
+    private CompatUICallback mCallback;
 
     /** Only show once automatically in the process life. */
     private boolean mHasShownHint;
-    /** Indicates if the keyguard is currently occluded, in which case size compat UIs shouldn't
+    /** Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
      * be shown. */
     private boolean mKeyguardOccluded;
 
-    public SizeCompatUIController(Context context,
+    public CompatUIController(Context context,
             DisplayController displayController,
             DisplayInsetsController displayInsetsController,
             DisplayImeController imeController,
@@ -108,35 +108,36 @@
         mImeController.addPositionProcessor(this);
     }
 
-    public SizeCompatUI asSizeCompatUI() {
+    /** Returns implementation of {@link CompatUI}. */
+    public CompatUI asCompatUI() {
         return mImpl;
     }
 
     /** Sets the callback for UI interactions. */
-    public void setSizeCompatUICallback(SizeCompatUICallback callback) {
+    public void setCompatUICallback(CompatUICallback callback) {
         mCallback = callback;
     }
 
     /**
-     * Called when the Task info changed. Creates and updates the size compat UI if there is an
+     * Called when the Task info changed. Creates and updates the compat UI if there is an
      * activity in size compat, or removes the UI if there is no size compat activity.
      *
      * @param displayId display the task and activity are in.
      * @param taskId task the activity is in.
-     * @param taskConfig task config to place the size compat UI with.
+     * @param taskConfig task config to place the compat UI with.
      * @param taskListener listener to handle the Task Surface placement.
      */
-    public void onSizeCompatInfoChanged(int displayId, int taskId,
+    public void onCompatInfoChanged(int displayId, int taskId,
             @Nullable Configuration taskConfig,
             @Nullable ShellTaskOrganizer.TaskListener taskListener) {
         if (taskConfig == null || taskListener == null) {
-            // Null token means the current foreground activity is not in size compatibility mode.
+            // Null token means the current foreground activity is not in compatibility mode.
             removeLayout(taskId);
         } else if (mActiveLayouts.contains(taskId)) {
             // UI already exists, update the UI layout.
             updateLayout(taskId, taskConfig, taskListener);
         } else {
-            // Create a new size compat UI.
+            // Create a new compat UI.
             createLayout(displayId, taskId, taskConfig, taskListener);
         }
     }
@@ -151,7 +152,7 @@
         mDisplayContextCache.remove(displayId);
         removeOnInsetsChangedListener(displayId);
 
-        // Remove all size compat UIs on the removed display.
+        // Remove all compat UIs on the removed display.
         final List<Integer> toRemoveTaskIds = new ArrayList<>();
         forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId()));
         for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) {
@@ -194,7 +195,7 @@
             mDisplaysWithIme.remove(displayId);
         }
 
-        // Hide the size compat UIs when input method is showing.
+        // Hide the compat UIs when input method is showing.
         forAllLayoutsOnDisplay(displayId,
                 layout -> layout.updateVisibility(showOnDisplay(displayId)));
     }
@@ -202,7 +203,7 @@
     @VisibleForTesting
     void onKeyguardOccludedChanged(boolean occluded) {
         mKeyguardOccluded = occluded;
-        // Hide the size compat UIs when keyguard is occluded.
+        // Hide the compat UIs when keyguard is occluded.
         forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId())));
     }
 
@@ -222,34 +223,34 @@
             return;
         }
 
-        final SizeCompatUILayout layout = createLayout(context, displayId, taskId, taskConfig,
-                taskListener);
-        mActiveLayouts.put(taskId, layout);
-        layout.createSizeCompatButton(showOnDisplay(displayId));
+        final CompatUIWindowManager compatUIWindowManager =
+                createLayout(context, displayId, taskId, taskConfig, taskListener);
+        mActiveLayouts.put(taskId, compatUIWindowManager);
+        compatUIWindowManager.createLayout(showOnDisplay(displayId));
     }
 
     @VisibleForTesting
-    SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
+    CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
             Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
-        final SizeCompatUILayout layout = new SizeCompatUILayout(mSyncQueue, mCallback, context,
-                taskConfig, taskId, taskListener, mDisplayController.getDisplayLayout(displayId),
-                mHasShownHint);
+        final CompatUIWindowManager compatUIWindowManager = new CompatUIWindowManager(context,
+                taskConfig, mSyncQueue, mCallback, taskId, taskListener,
+                mDisplayController.getDisplayLayout(displayId), mHasShownHint);
         // Only show hint for the first time.
         mHasShownHint = true;
-        return layout;
+        return compatUIWindowManager;
     }
 
     private void updateLayout(int taskId, Configuration taskConfig,
             ShellTaskOrganizer.TaskListener taskListener) {
-        final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+        final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
         if (layout == null) {
             return;
         }
-        layout.updateSizeCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId()));
+        layout.updateCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId()));
     }
 
     private void removeLayout(int taskId) {
-        final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+        final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
         if (layout != null) {
             layout.release();
             mActiveLayouts.remove(taskId);
@@ -275,19 +276,19 @@
         return context;
     }
 
-    private void forAllLayoutsOnDisplay(int displayId, Consumer<SizeCompatUILayout> callback) {
+    private void forAllLayoutsOnDisplay(int displayId, Consumer<CompatUIWindowManager> callback) {
         forAllLayouts(layout -> layout.getDisplayId() == displayId, callback);
     }
 
-    private void forAllLayouts(Consumer<SizeCompatUILayout> callback) {
+    private void forAllLayouts(Consumer<CompatUIWindowManager> callback) {
         forAllLayouts(layout -> true, callback);
     }
 
-    private void forAllLayouts(Predicate<SizeCompatUILayout> condition,
-            Consumer<SizeCompatUILayout> callback) {
+    private void forAllLayouts(Predicate<CompatUIWindowManager> condition,
+            Consumer<CompatUIWindowManager> callback) {
         for (int i = 0; i < mActiveLayouts.size(); i++) {
             final int taskId = mActiveLayouts.keyAt(i);
-            final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+            final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
             if (layout != null && condition.test(layout)) {
                 callback.accept(layout);
             }
@@ -298,11 +299,11 @@
      * The interface for calls from outside the Shell, within the host process.
      */
     @ExternalThread
-    private class SizeCompatUIImpl implements SizeCompatUI {
+    private class CompatUIImpl implements CompatUI {
         @Override
         public void onKeyguardOccludedChanged(boolean occluded) {
             mMainExecutor.execute(() -> {
-                SizeCompatUIController.this.onKeyguardOccludedChanged(occluded);
+                CompatUIController.this.onKeyguardOccludedChanged(occluded);
             });
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
new file mode 100644
index 0000000..ea4f209
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.wm.shell.R;
+
+/**
+ * Container for compat UI controls.
+ */
+public class CompatUILayout extends LinearLayout {
+
+    private CompatUIWindowManager mWindowManager;
+
+    public CompatUILayout(Context context) {
+        this(context, null);
+    }
+
+    public CompatUILayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    void inject(CompatUIWindowManager windowManager) {
+        mWindowManager = windowManager;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        // Need to relayout after changes like hiding / showing a hint since they affect size.
+        // Doing this directly in setSizeCompatHintVisibility can result in flaky animation.
+        mWindowManager.relayout();
+    }
+
+    void setSizeCompatHintVisibility(boolean show) {
+        final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint);
+        int visibility = show ? View.VISIBLE : View.GONE;
+        if (sizeCompatHint.getVisibility() == visibility) {
+            return;
+        }
+        sizeCompatHint.setVisibility(visibility);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        final ImageButton restartButton = findViewById(R.id.size_compat_restart_button);
+        restartButton.setOnClickListener(view -> mWindowManager.onRestartButtonClicked());
+        restartButton.setOnLongClickListener(view -> {
+            mWindowManager.onRestartButtonLongClicked();
+            return true;
+        });
+
+        final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint);
+        ((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text))
+                .setText(R.string.restart_button_description);
+        sizeCompatHint.setOnClickListener(view -> setSizeCompatHintVisibility(/* show= */ false));
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
new file mode 100644
index 0000000..997ad04
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.util.Log;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+/**
+ * Holds view hierarchy of a root surface and helps to inflate and manage layout for compat
+ * controls.
+ */
+class CompatUIWindowManager extends WindowlessWindowManager {
+
+    private static final String TAG = "CompatUIWindowManager";
+
+    private final SyncTransactionQueue mSyncQueue;
+    private final CompatUIController.CompatUICallback mCallback;
+    private final int mDisplayId;
+    private final int mTaskId;
+    private final Rect mStableBounds;
+
+    private Context mContext;
+    private Configuration mTaskConfig;
+    private ShellTaskOrganizer.TaskListener mTaskListener;
+    private DisplayLayout mDisplayLayout;
+
+    @VisibleForTesting
+    boolean mShouldShowHint;
+
+    @Nullable
+    @VisibleForTesting
+    CompatUILayout mCompatUILayout;
+
+    @Nullable
+    private SurfaceControlViewHost mViewHost;
+    @Nullable
+    private SurfaceControl mLeash;
+
+    CompatUIWindowManager(Context context, Configuration taskConfig,
+            SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback,
+            int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
+            boolean hasShownHint) {
+        super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
+        mContext = context;
+        mSyncQueue = syncQueue;
+        mCallback = callback;
+        mTaskConfig = taskConfig;
+        mDisplayId = mContext.getDisplayId();
+        mTaskId = taskId;
+        mTaskListener = taskListener;
+        mDisplayLayout = displayLayout;
+        mShouldShowHint = !hasShownHint;
+        mStableBounds = new Rect();
+        mDisplayLayout.getStableBounds(mStableBounds);
+    }
+
+    @Override
+    public void setConfiguration(Configuration configuration) {
+        super.setConfiguration(configuration);
+        mContext = mContext.createConfigurationContext(configuration);
+    }
+
+    @Override
+    protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+        // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
+        final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+                .setContainerLayer()
+                .setName("CompatUILeash")
+                .setHidden(false)
+                .setCallsite("CompatUIWindowManager#attachToParentSurface");
+        attachToParentSurface(builder);
+        mLeash = builder.build();
+        b.setParent(mLeash);
+    }
+
+    /** Creates the layout for compat controls. */
+    void createLayout(boolean show) {
+        if (!show || mCompatUILayout != null) {
+            // Wait until compat controls should be visible.
+            return;
+        }
+
+        initCompatUi();
+        updateSurfacePosition();
+
+        mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
+    }
+
+    /** Called when compat info changed. */
+    void updateCompatInfo(Configuration taskConfig,
+            ShellTaskOrganizer.TaskListener taskListener, boolean show) {
+        final Configuration prevTaskConfig = mTaskConfig;
+        final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
+        mTaskConfig = taskConfig;
+        mTaskListener = taskListener;
+
+        // Update configuration.
+        mContext = mContext.createConfigurationContext(taskConfig);
+        setConfiguration(taskConfig);
+
+        if (mCompatUILayout == null || prevTaskListener != taskListener) {
+            // TaskListener changed, recreate the layout for new surface parent.
+            release();
+            createLayout(show);
+            return;
+        }
+
+        if (!taskConfig.windowConfiguration.getBounds()
+                .equals(prevTaskConfig.windowConfiguration.getBounds())) {
+            // Reposition the UI surfaces.
+            updateSurfacePosition();
+        }
+
+        if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
+            // Update layout for RTL.
+            mCompatUILayout.setLayoutDirection(taskConfig.getLayoutDirection());
+            updateSurfacePosition();
+        }
+    }
+
+    /** Called when the visibility of the UI should change. */
+    void updateVisibility(boolean show) {
+        if (mCompatUILayout == null) {
+            // Layout may not have been created because it was hidden previously.
+            createLayout(show);
+            return;
+        }
+
+        // Hide compat UIs when IME is showing.
+        final int newVisibility = show ? View.VISIBLE : View.GONE;
+        if (mCompatUILayout.getVisibility() != newVisibility) {
+            mCompatUILayout.setVisibility(newVisibility);
+        }
+    }
+
+    /** Called when display layout changed. */
+    void updateDisplayLayout(DisplayLayout displayLayout) {
+        final Rect prevStableBounds = mStableBounds;
+        final Rect curStableBounds = new Rect();
+        displayLayout.getStableBounds(curStableBounds);
+        mDisplayLayout = displayLayout;
+        if (!prevStableBounds.equals(curStableBounds)) {
+            // Stable bounds changed, update UI surface positions.
+            updateSurfacePosition();
+            mStableBounds.set(curStableBounds);
+        }
+    }
+
+    /** Called when it is ready to be placed compat UI surface. */
+    void attachToParentSurface(SurfaceControl.Builder b) {
+        mTaskListener.attachChildSurfaceToTask(mTaskId, b);
+    }
+
+    /** Called when the restart button is clicked. */
+    void onRestartButtonClicked() {
+        mCallback.onSizeCompatRestartButtonClicked(mTaskId);
+    }
+
+    /** Called when the restart button is long clicked. */
+    void onRestartButtonLongClicked() {
+        if (mCompatUILayout == null) {
+            return;
+        }
+        mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
+    }
+
+    int getDisplayId() {
+        return mDisplayId;
+    }
+
+    int getTaskId() {
+        return mTaskId;
+    }
+
+    /** Releases the surface control and tears down the view hierarchy. */
+    void release() {
+        mCompatUILayout = null;
+
+        if (mViewHost != null) {
+            mViewHost.release();
+            mViewHost = null;
+        }
+
+        if (mLeash != null) {
+            final SurfaceControl leash = mLeash;
+            mSyncQueue.runInSync(t -> t.remove(leash));
+            mLeash = null;
+        }
+    }
+
+    void relayout() {
+        mViewHost.relayout(getWindowLayoutParams());
+        updateSurfacePosition();
+    }
+
+    @VisibleForTesting
+    void updateSurfacePosition() {
+        if (mCompatUILayout == null || mLeash == null) {
+            return;
+        }
+
+        // Use stable bounds to prevent controls from overlapping with system bars.
+        final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
+        final Rect stableBounds = new Rect();
+        mDisplayLayout.getStableBounds(stableBounds);
+        stableBounds.intersect(taskBounds);
+
+        // Position of the button in the container coordinate.
+        final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+                ? stableBounds.left - taskBounds.left
+                : stableBounds.right - taskBounds.left - mCompatUILayout.getMeasuredWidth();
+        final int positionY = stableBounds.bottom - taskBounds.top
+                - mCompatUILayout.getMeasuredHeight();
+
+        updateSurfacePosition(positionX, positionY);
+    }
+
+    private int getLayoutDirection() {
+        return mContext.getResources().getConfiguration().getLayoutDirection();
+    }
+
+    private void updateSurfacePosition(int positionX, int positionY) {
+        mSyncQueue.runInSync(t -> {
+            if (mLeash == null || !mLeash.isValid()) {
+                Log.w(TAG, "The leash has been released.");
+                return;
+            }
+            t.setPosition(mLeash, positionX, positionY);
+            // The compat UI should be the topmost child of the Task in case there can be more
+            // than one children.
+            t.setLayer(mLeash, Integer.MAX_VALUE);
+        });
+    }
+
+    /** Inflates {@link CompatUILayout} on to the root surface. */
+    private void initCompatUi() {
+        if (mViewHost != null) {
+            throw new IllegalStateException(
+                    "A UI has already been created with this window manager.");
+        }
+
+        // Construction extracted into the separate methods to allow injection for tests.
+        mViewHost = createSurfaceViewHost();
+        mCompatUILayout = inflateCompatUILayout();
+        mCompatUILayout.inject(this);
+
+        mCompatUILayout.setSizeCompatHintVisibility(mShouldShowHint);
+
+        mViewHost.setView(mCompatUILayout, getWindowLayoutParams());
+
+        // Only show by default for the first time.
+        mShouldShowHint = false;
+    }
+
+    @VisibleForTesting
+    CompatUILayout inflateCompatUILayout() {
+        return (CompatUILayout) LayoutInflater.from(mContext)
+                .inflate(R.layout.compat_ui_layout, null);
+    }
+
+    @VisibleForTesting
+    SurfaceControlViewHost createSurfaceViewHost() {
+        return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+    }
+
+    /** Gets the layout params. */
+    private WindowManager.LayoutParams getWindowLayoutParams() {
+        // Measure how big the hint is since its size depends on the text size.
+        mCompatUILayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+        final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
+                // Cannot be wrap_content as this determines the actual window size
+                mCompatUILayout.getMeasuredWidth(), mCompatUILayout.getMeasuredHeight(),
+                TYPE_APPLICATION_OVERLAY,
+                FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
+                PixelFormat.TRANSLUCENT);
+        winParams.token = new Binder();
+        winParams.setTitle(CompatUILayout.class.getSimpleName() + mTaskId);
+        winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+        return winParams;
+    }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 9500e8a..6d4b2fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -54,6 +54,8 @@
 import com.android.wm.shell.common.annotations.ShellAnimationThread;
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.compatui.CompatUI;
+import com.android.wm.shell.compatui.CompatUIController;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
 import com.android.wm.shell.draganddrop.DragAndDrop;
@@ -75,8 +77,6 @@
 import com.android.wm.shell.pip.phone.PipTouchHandler;
 import com.android.wm.shell.recents.RecentTasks;
 import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
-import com.android.wm.shell.sizecompatui.SizeCompatUIController;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.startingsurface.StartingSurface;
@@ -173,25 +173,25 @@
     @Provides
     static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
             Context context,
-            SizeCompatUIController sizeCompatUI,
+            CompatUIController compatUI,
             Optional<RecentTasksController> recentTasksOptional
     ) {
-        return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI, recentTasksOptional);
+        return new ShellTaskOrganizer(mainExecutor, context, compatUI, recentTasksOptional);
     }
 
     @WMSingleton
     @Provides
-    static SizeCompatUI provideSizeCompatUI(SizeCompatUIController sizeCompatUIController) {
-        return sizeCompatUIController.asSizeCompatUI();
+    static CompatUI provideCompatUI(CompatUIController compatUIController) {
+        return compatUIController.asCompatUI();
     }
 
     @WMSingleton
     @Provides
-    static SizeCompatUIController provideSizeCompatUIController(Context context,
+    static CompatUIController provideCompatUIController(Context context,
             DisplayController displayController, DisplayInsetsController displayInsetsController,
             DisplayImeController imeController, SyncTransactionQueue syncQueue,
             @ShellMainThread ShellExecutor mainExecutor) {
-        return new SizeCompatUIController(context, displayController, displayInsetsController,
+        return new CompatUIController(context, displayController, displayInsetsController,
                 imeController, syncQueue, mainExecutor);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index b65a2e4..8e6c05d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -151,7 +151,13 @@
                 final Rect rightHitRegion = new Rect();
                 final Rect rightDrawRegion = bottomOrRightBounds;
 
-                displayRegion.splitVertically(leftHitRegion, rightHitRegion);
+                // If we have existing split regions use those bounds, otherwise split it 50/50
+                if (inSplitScreen) {
+                    leftHitRegion.set(topOrLeftBounds);
+                    rightHitRegion.set(bottomOrRightBounds);
+                } else {
+                    displayRegion.splitVertically(leftHitRegion, rightHitRegion);
+                }
 
                 mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, leftDrawRegion));
                 mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, rightDrawRegion));
@@ -162,8 +168,13 @@
                 final Rect bottomHitRegion = new Rect();
                 final Rect bottomDrawRegion = bottomOrRightBounds;
 
-                displayRegion.splitHorizontally(
-                        topHitRegion, bottomHitRegion);
+                // If we have existing split regions use those bounds, otherwise split it 50/50
+                if (inSplitScreen) {
+                    topHitRegion.set(topOrLeftBounds);
+                    bottomHitRegion.set(bottomOrRightBounds);
+                } else {
+                    displayRegion.splitHorizontally(topHitRegion, bottomHitRegion);
+                }
 
                 mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topDrawRegion));
                 mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomDrawRegion));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index 67f9062..fd3be2b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -17,7 +17,9 @@
 package com.android.wm.shell.draganddrop;
 
 import static android.app.StatusBarManager.DISABLE_NONE;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 
+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 android.animation.Animator;
@@ -32,11 +34,11 @@
 import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.Insets;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.RemoteException;
 import android.view.DragEvent;
 import android.view.SurfaceControl;
-import android.view.ViewGroup;
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type;
 import android.widget.LinearLayout;
@@ -73,6 +75,7 @@
     private DropZoneView mDropZoneView2;
 
     private int mDisplayMargin;
+    private int mDividerSize;
     private Insets mInsets = Insets.NONE;
 
     private boolean mIsShowing;
@@ -89,13 +92,15 @@
 
         mDisplayMargin = context.getResources().getDimensionPixelSize(
                 R.dimen.drop_layout_display_margin);
+        mDividerSize = context.getResources().getDimensionPixelSize(
+                R.dimen.split_divider_bar_width);
 
         mDropZoneView1 = new DropZoneView(context);
         mDropZoneView2 = new DropZoneView(context);
-        addView(mDropZoneView1, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT));
-        addView(mDropZoneView2, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT));
+        addView(mDropZoneView1, new LinearLayout.LayoutParams(MATCH_PARENT,
+                MATCH_PARENT));
+        addView(mDropZoneView2, new LinearLayout.LayoutParams(MATCH_PARENT,
+                MATCH_PARENT));
         ((LayoutParams) mDropZoneView1.getLayoutParams()).weight = 1;
         ((LayoutParams) mDropZoneView2.getLayoutParams()).weight = 1;
         updateContainerMargins();
@@ -165,44 +170,82 @@
         mHasDropped = false;
         mCurrentTarget = null;
 
-        List<ActivityManager.RunningTaskInfo> tasks = null;
-        // Figure out the splashscreen info for the existing task(s).
-        try {
-            tasks = ActivityTaskManager.getService().getTasks(2,
-                            false /* filterOnlyVisibleRecents */,
-                            false /* keepIntentExtra */);
-        } catch (RemoteException e) {
-            // don't show an icon / will just use the defaults
-        }
-        if (tasks != null && !tasks.isEmpty()) {
-            ActivityManager.RunningTaskInfo taskInfo1 = tasks.get(0);
-            Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo);
-            int bgColor1 = getResizingBackgroundColor(taskInfo1);
-
-            boolean alreadyInSplit = mSplitScreenController != null
-                    && mSplitScreenController.isSplitScreenVisible();
-            if (alreadyInSplit && tasks.size() > 1) {
-                ActivityManager.RunningTaskInfo taskInfo2 = tasks.get(1);
-                Drawable icon2 = mIconProvider.getIcon(taskInfo2.topActivityInfo);
-                int bgColor2 = getResizingBackgroundColor(taskInfo2);
-
-                // figure out which task is on which side
-                int splitPosition1 = mSplitScreenController.getSplitPosition(taskInfo1.taskId);
-                boolean isTask1TopOrLeft = splitPosition1 == SPLIT_POSITION_TOP_OR_LEFT;
-                if (isTask1TopOrLeft) {
-                    mDropZoneView1.setAppInfo(bgColor1, icon1);
-                    mDropZoneView2.setAppInfo(bgColor2, icon2);
-                } else {
-                    mDropZoneView2.setAppInfo(bgColor1, icon1);
-                    mDropZoneView1.setAppInfo(bgColor2, icon2);
-                }
-            } else {
+        boolean alreadyInSplit = mSplitScreenController != null
+                && mSplitScreenController.isSplitScreenVisible();
+        if (!alreadyInSplit) {
+            List<ActivityManager.RunningTaskInfo> tasks = null;
+            // Figure out the splashscreen info for the existing task.
+            try {
+                tasks = ActivityTaskManager.getService().getTasks(1,
+                        false /* filterOnlyVisibleRecents */,
+                        false /* keepIntentExtra */);
+            } catch (RemoteException e) {
+                // don't show an icon / will just use the defaults
+            }
+            if (tasks != null && !tasks.isEmpty()) {
+                ActivityManager.RunningTaskInfo taskInfo1 = tasks.get(0);
+                Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo);
+                int bgColor1 = getResizingBackgroundColor(taskInfo1);
                 mDropZoneView1.setAppInfo(bgColor1, icon1);
                 mDropZoneView2.setAppInfo(bgColor1, icon1);
+                updateDropZoneSizes(null, null); // passing null splits the views evenly
             }
+        } else {
+            // We're already in split so get taskInfo from the controller to populate icon / color.
+            ActivityManager.RunningTaskInfo topOrLeftTask =
+                    mSplitScreenController.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+            ActivityManager.RunningTaskInfo bottomOrRightTask =
+                    mSplitScreenController.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+            if (topOrLeftTask != null && bottomOrRightTask != null) {
+                Drawable topOrLeftIcon = mIconProvider.getIcon(topOrLeftTask.topActivityInfo);
+                int topOrLeftColor = getResizingBackgroundColor(topOrLeftTask);
+                Drawable bottomOrRightIcon = mIconProvider.getIcon(
+                        bottomOrRightTask.topActivityInfo);
+                int bottomOrRightColor = getResizingBackgroundColor(bottomOrRightTask);
+                mDropZoneView1.setAppInfo(topOrLeftColor, topOrLeftIcon);
+                mDropZoneView2.setAppInfo(bottomOrRightColor, bottomOrRightIcon);
+            }
+
+            // Update the dropzones to match existing split sizes
+            Rect topOrLeftBounds = new Rect();
+            Rect bottomOrRightBounds = new Rect();
+            mSplitScreenController.getStageBounds(topOrLeftBounds, bottomOrRightBounds);
+            updateDropZoneSizes(topOrLeftBounds, bottomOrRightBounds);
         }
     }
 
+    /**
+     * Sets the size of the two drop zones based on the provided bounds. The divider sits between
+     * the views and its size is included in the calculations.
+     *
+     * @param bounds1 bounds to apply to the first dropzone view, null if split in half.
+     * @param bounds2 bounds to apply to the second dropzone view, null if split in half.
+     */
+    private void updateDropZoneSizes(Rect bounds1, Rect bounds2) {
+        final int orientation = getResources().getConfiguration().orientation;
+        final boolean isPortrait = orientation == Configuration.ORIENTATION_PORTRAIT;
+        final int halfDivider = mDividerSize / 2;
+        final LinearLayout.LayoutParams dropZoneView1 =
+                (LayoutParams) mDropZoneView1.getLayoutParams();
+        final LinearLayout.LayoutParams dropZoneView2 =
+                (LayoutParams) mDropZoneView2.getLayoutParams();
+        if (isPortrait) {
+            dropZoneView1.width = MATCH_PARENT;
+            dropZoneView2.width = MATCH_PARENT;
+            dropZoneView1.height = bounds1 != null ? bounds1.height() + halfDivider : MATCH_PARENT;
+            dropZoneView2.height = bounds2 != null ? bounds2.height() + halfDivider : MATCH_PARENT;
+        } else {
+            dropZoneView1.width = bounds1 != null ? bounds1.width() + halfDivider : MATCH_PARENT;
+            dropZoneView2.width = bounds2 != null ? bounds2.width() + halfDivider : MATCH_PARENT;
+            dropZoneView1.height = MATCH_PARENT;
+            dropZoneView2.height = MATCH_PARENT;
+        }
+        dropZoneView1.weight = bounds1 != null ? 0 : 1;
+        dropZoneView2.weight = bounds2 != null ? 0 : 1;
+        mDropZoneView1.setLayoutParams(dropZoneView1);
+        mDropZoneView2.setLayoutParams(dropZoneView2);
+    }
+
     public void show() {
         mIsShowing = true;
         recomputeDropTargets();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 6d4773b..c0734e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -112,11 +112,17 @@
     default void showPictureInPictureMenu() {}
 
     /**
-     * Called by NavigationBar in order to listen in for PiP bounds change. This is mostly used
-     * for times where the PiP bounds could conflict with SystemUI elements, such as a stashed
-     * PiP and the Back-from-Edge gesture.
+     * Called by NavigationBar and TaskbarDelegate in order to listen in for PiP bounds change. This
+     * is mostly used for times where the PiP bounds could conflict with SystemUI elements, such as
+     * a stashed PiP and the Back-from-Edge gesture.
      */
-    default void setPipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
+    default void addPipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
+
+    /**
+     * Remove a callback added previously. This is used when NavigationBar is removed from the
+     * view hierarchy or destroyed.
+     */
+    default void removePipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
 
     /**
      * Dump the current state and information if need.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index e3674dc..b3558ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -38,6 +38,8 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
 
@@ -89,7 +91,7 @@
 
     private @Nullable Runnable mOnMinimalSizeChangeCallback;
     private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
-    private @Nullable Consumer<Rect> mOnPipExclusionBoundsChangeCallback;
+    private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
 
     public PipBoundsState(@NonNull Context context) {
         mContext = context;
@@ -108,8 +110,8 @@
     /** Set the current PIP bounds. */
     public void setBounds(@NonNull Rect bounds) {
         mBounds.set(bounds);
-        if (mOnPipExclusionBoundsChangeCallback != null) {
-            mOnPipExclusionBoundsChangeCallback.accept(bounds);
+        for (Consumer<Rect> callback : mOnPipExclusionBoundsChangeCallbacks) {
+            callback.accept(bounds);
         }
     }
 
@@ -407,17 +409,25 @@
     }
 
     /**
-     * Set a callback to watch out for PiP bounds. This is mostly used by SystemUI's
+     * Add a callback to watch out for PiP bounds. This is mostly used by SystemUI's
      * Back-gesture handler, to avoid conflicting with PiP when it's stashed.
      */
-    public void setPipExclusionBoundsChangeCallback(
+    public void addPipExclusionBoundsChangeCallback(
             @Nullable Consumer<Rect> onPipExclusionBoundsChangeCallback) {
-        mOnPipExclusionBoundsChangeCallback = onPipExclusionBoundsChangeCallback;
-        if (mOnPipExclusionBoundsChangeCallback != null) {
-            mOnPipExclusionBoundsChangeCallback.accept(getBounds());
+        mOnPipExclusionBoundsChangeCallbacks.add(onPipExclusionBoundsChangeCallback);
+        for (Consumer<Rect> callback : mOnPipExclusionBoundsChangeCallbacks) {
+            callback.accept(getBounds());
         }
     }
 
+    /**
+     * Remove a callback that was previously added.
+     */
+    public void removePipExclusionBoundsChangeCallback(
+            @Nullable Consumer<Rect> onPipExclusionBoundsChangeCallback) {
+        mOnPipExclusionBoundsChangeCallbacks.remove(onPipExclusionBoundsChangeCallback);
+    }
+
     /** Source of truth for the current bounds of PIP that may be in motion. */
     public static class MotionBoundsState {
         /** The bounds used when PIP is in motion (e.g. during a drag or animation) */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 854fc60e..f0b2716 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -778,8 +778,8 @@
     }
 
     @Override
-    public boolean supportSizeCompatUI() {
-        // PIP doesn't support size compat.
+    public boolean supportCompatUI() {
+        // PIP doesn't support compat.
         return false;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index eb512af..101a55d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -191,6 +191,7 @@
         mSystemWindows.addView(mPipMenuView,
                 getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
                 0, SHELL_ROOT_LAYER_PIP);
+        setShellRootAccessibilityWindow();
     }
 
     private void detachPipMenuView() {
@@ -546,6 +547,10 @@
             mListeners.forEach(l -> l.onPipMenuStateChangeFinish(menuState));
         }
         mMenuState = menuState;
+        setShellRootAccessibilityWindow();
+    }
+
+    private void setShellRootAccessibilityWindow() {
         switch (mMenuState) {
             case MENU_STATE_NONE:
                 mSystemWindows.setShellRootAccessibilityWindow(0, SHELL_ROOT_LAYER_PIP, null);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 26fd962..a41fd84 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -843,9 +843,16 @@
         }
 
         @Override
-        public void setPipExclusionBoundsChangeListener(Consumer<Rect> listener) {
+        public void addPipExclusionBoundsChangeListener(Consumer<Rect> listener) {
             mMainExecutor.execute(() -> {
-                mPipBoundsState.setPipExclusionBoundsChangeCallback(listener);
+                mPipBoundsState.addPipExclusionBoundsChangeCallback(listener);
+            });
+        }
+
+        @Override
+        public void removePipExclusionBoundsChangeListener(Consumer<Rect> listener) {
+            mMainExecutor.execute(() -> {
+                mPipBoundsState.removePipExclusionBoundsChangeCallback(listener);
             });
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 0fbdf90..92a3598 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -120,6 +120,11 @@
         mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
         mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
 
+        if (mTargetViewContainer != null) {
+            // init can be called multiple times, remove the old one from view hierarchy first.
+            cleanUpDismissTarget();
+        }
+
         mTargetView = new DismissCircleView(mContext);
         mTargetViewContainer = new FrameLayout(mContext);
         mTargetViewContainer.setBackgroundDrawable(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 82e8273..da4bbe8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -189,7 +189,7 @@
         mEnterSplitButton = findViewById(R.id.enter_split);
         mEnterSplitButton.setAlpha(0);
         mEnterSplitButton.setOnClickListener(v -> {
-            if (mMenuContainer.getAlpha() != 0) {
+            if (mEnterSplitButton.getAlpha() != 0) {
                 enterSplit();
             }
         });
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
deleted file mode 100644
index ff6f913..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.R;
-
-/** Popup to show the hint about the {@link SizeCompatRestartButton}. */
-public class SizeCompatHintPopup extends FrameLayout implements View.OnClickListener {
-
-    private SizeCompatUILayout mLayout;
-
-    public SizeCompatHintPopup(Context context) {
-        super(context);
-    }
-
-    public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public SizeCompatHintPopup(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    void inject(SizeCompatUILayout layout) {
-        mLayout = layout;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        final LinearLayout hintPopup = findViewById(R.id.size_compat_hint_popup);
-        hintPopup.setOnClickListener(this);
-    }
-
-    @Override
-    public void onClick(View v) {
-        mLayout.dismissHint();
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
deleted file mode 100644
index d75fe517..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.R;
-
-/** Button to restart the size compat activity. */
-public class SizeCompatRestartButton extends FrameLayout implements View.OnClickListener,
-        View.OnLongClickListener {
-
-    private SizeCompatUILayout mLayout;
-
-    public SizeCompatRestartButton(@NonNull Context context) {
-        super(context);
-    }
-
-    public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs,
-            int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs,
-            int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    void inject(SizeCompatUILayout layout) {
-        mLayout = layout;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        final ImageButton restartButton = findViewById(R.id.size_compat_restart_button);
-        restartButton.setOnClickListener(this);
-        restartButton.setOnLongClickListener(this);
-    }
-
-    @Override
-    public void onClick(View v) {
-        mLayout.onRestartButtonClicked();
-    }
-
-    @Override
-    public boolean onLongClick(View v) {
-        mLayout.onRestartButtonLongClicked();
-        return true;
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
deleted file mode 100644
index c35b89a..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.util.Log;
-import android.view.SurfaceControl;
-import android.view.View;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-/**
- * Records and handles layout of size compat UI on a task with size compat activity. Helps to
- * calculate proper bounds when configuration or UI position changes.
- */
-class SizeCompatUILayout {
-    private static final String TAG = "SizeCompatUILayout";
-
-    final SyncTransactionQueue mSyncQueue;
-    private final SizeCompatUIController.SizeCompatUICallback mCallback;
-    private Context mContext;
-    private Configuration mTaskConfig;
-    private final int mDisplayId;
-    private final int mTaskId;
-    private ShellTaskOrganizer.TaskListener mTaskListener;
-    private DisplayLayout mDisplayLayout;
-    private final Rect mStableBounds;
-    private final int mButtonWidth;
-    private final int mButtonHeight;
-    private final int mPopupOffsetX;
-    private final int mPopupOffsetY;
-
-    @VisibleForTesting
-    final SizeCompatUIWindowManager mButtonWindowManager;
-    @VisibleForTesting
-    @Nullable
-    SizeCompatUIWindowManager mHintWindowManager;
-    @VisibleForTesting
-    @Nullable
-    SizeCompatRestartButton mButton;
-    @VisibleForTesting
-    @Nullable
-    SizeCompatHintPopup mHint;
-    @VisibleForTesting
-    boolean mShouldShowHint;
-
-    SizeCompatUILayout(SyncTransactionQueue syncQueue,
-            SizeCompatUIController.SizeCompatUICallback callback, Context context,
-            Configuration taskConfig, int taskId, ShellTaskOrganizer.TaskListener taskListener,
-            DisplayLayout displayLayout, boolean hasShownHint) {
-        mSyncQueue = syncQueue;
-        mCallback = callback;
-        mContext = context.createConfigurationContext(taskConfig);
-        mTaskConfig = taskConfig;
-        mDisplayId = mContext.getDisplayId();
-        mTaskId = taskId;
-        mTaskListener = taskListener;
-        mDisplayLayout = displayLayout;
-        mShouldShowHint = !hasShownHint;
-        mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);
-
-        mStableBounds = new Rect();
-        mDisplayLayout.getStableBounds(mStableBounds);
-
-        final Resources resources = mContext.getResources();
-        mButtonWidth = resources.getDimensionPixelSize(R.dimen.size_compat_button_width);
-        mButtonHeight = resources.getDimensionPixelSize(R.dimen.size_compat_button_height);
-        mPopupOffsetX = (mButtonWidth / 2) - resources.getDimensionPixelSize(
-                R.dimen.size_compat_hint_corner_radius) - (resources.getDimensionPixelSize(
-                R.dimen.size_compat_hint_point_width) / 2);
-        mPopupOffsetY = mButtonHeight;
-    }
-
-    /** Creates the activity restart button window. */
-    void createSizeCompatButton(boolean show) {
-        if (!show || mButton != null) {
-            // Wait until button should be visible.
-            return;
-        }
-        mButton = mButtonWindowManager.createSizeCompatButton();
-        updateButtonSurfacePosition();
-
-        if (mShouldShowHint) {
-            // Only show by default for the first time.
-            mShouldShowHint = false;
-            createSizeCompatHint();
-        }
-
-        mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
-    }
-
-    /** Creates the restart button hint window. */
-    private void createSizeCompatHint() {
-        if (mHint != null) {
-            // Hint already shown.
-            return;
-        }
-        mHintWindowManager = createHintWindowManager();
-        mHint = mHintWindowManager.createSizeCompatHint();
-        updateHintSurfacePosition();
-    }
-
-    @VisibleForTesting
-    SizeCompatUIWindowManager createHintWindowManager() {
-        return new SizeCompatUIWindowManager(mContext, mTaskConfig, this);
-    }
-
-    /** Dismisses the hint window. */
-    void dismissHint() {
-        mHint = null;
-        if (mHintWindowManager != null) {
-            mHintWindowManager.release();
-            mHintWindowManager = null;
-        }
-    }
-
-    /** Releases the UI windows. */
-    void release() {
-        dismissHint();
-        mButton = null;
-        mButtonWindowManager.release();
-    }
-
-    /** Called when size compat info changed. */
-    void updateSizeCompatInfo(Configuration taskConfig,
-            ShellTaskOrganizer.TaskListener taskListener, boolean show) {
-        final Configuration prevTaskConfig = mTaskConfig;
-        final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
-        mTaskConfig = taskConfig;
-        mTaskListener = taskListener;
-
-        // Update configuration.
-        mContext = mContext.createConfigurationContext(taskConfig);
-        mButtonWindowManager.setConfiguration(taskConfig);
-        if (mHintWindowManager != null) {
-            mHintWindowManager.setConfiguration(taskConfig);
-        }
-
-        if (mButton == null || prevTaskListener != taskListener) {
-            // TaskListener changed, recreate the button for new surface parent.
-            release();
-            createSizeCompatButton(show);
-            return;
-        }
-
-        if (!taskConfig.windowConfiguration.getBounds()
-                .equals(prevTaskConfig.windowConfiguration.getBounds())) {
-            // Reposition the UI surfaces.
-            updateAllSurfacePositions();
-        }
-
-        if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
-            // Update layout for RTL.
-            mButton.setLayoutDirection(taskConfig.getLayoutDirection());
-            updateButtonSurfacePosition();
-            if (mHint != null) {
-                mHint.setLayoutDirection(taskConfig.getLayoutDirection());
-                updateHintSurfacePosition();
-            }
-        }
-    }
-
-    /** Called when display layout changed. */
-    void updateDisplayLayout(DisplayLayout displayLayout) {
-        final Rect prevStableBounds = mStableBounds;
-        final Rect curStableBounds = new Rect();
-        displayLayout.getStableBounds(curStableBounds);
-        mDisplayLayout = displayLayout;
-        if (!prevStableBounds.equals(curStableBounds)) {
-            // Stable bounds changed, update UI surface positions.
-            updateAllSurfacePositions();
-            mStableBounds.set(curStableBounds);
-        }
-    }
-
-    /** Called when the visibility of the UI should change. */
-    void updateVisibility(boolean show) {
-        if (mButton == null) {
-            // Button may not have been created because it was hidden previously.
-            createSizeCompatButton(show);
-            return;
-        }
-
-        // Hide size compat UIs when IME is showing.
-        final int newVisibility = show ? View.VISIBLE : View.GONE;
-        if (mButton.getVisibility() != newVisibility) {
-            mButton.setVisibility(newVisibility);
-        }
-        if (mHint != null && mHint.getVisibility() != newVisibility) {
-            mHint.setVisibility(newVisibility);
-        }
-    }
-
-    /** Gets the layout params for restart button. */
-    WindowManager.LayoutParams getButtonWindowLayoutParams() {
-        final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
-                // Cannot be wrap_content as this determines the actual window size
-                mButtonWidth, mButtonHeight,
-                TYPE_APPLICATION_OVERLAY,
-                FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
-                PixelFormat.TRANSLUCENT);
-        winParams.token = new Binder();
-        winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + getTaskId());
-        winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
-        return winParams;
-    }
-
-    /** Gets the layout params for hint popup. */
-    WindowManager.LayoutParams getHintWindowLayoutParams(SizeCompatHintPopup hint) {
-        final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
-                // Cannot be wrap_content as this determines the actual window size
-                hint.getMeasuredWidth(), hint.getMeasuredHeight(),
-                TYPE_APPLICATION_OVERLAY,
-                FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
-                PixelFormat.TRANSLUCENT);
-        winParams.token = new Binder();
-        winParams.setTitle(SizeCompatHintPopup.class.getSimpleName() + getTaskId());
-        winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
-        winParams.windowAnimations = android.R.style.Animation_InputMethod;
-        return winParams;
-    }
-
-    /** Called when it is ready to be placed size compat UI surface. */
-    void attachToParentSurface(SurfaceControl.Builder b) {
-        mTaskListener.attachChildSurfaceToTask(mTaskId, b);
-    }
-
-    /** Called when the restart button is clicked. */
-    void onRestartButtonClicked() {
-        mCallback.onSizeCompatRestartButtonClicked(mTaskId);
-    }
-
-    /** Called when the restart button is long clicked. */
-    void onRestartButtonLongClicked() {
-        createSizeCompatHint();
-    }
-
-    private void updateAllSurfacePositions() {
-        updateButtonSurfacePosition();
-        updateHintSurfacePosition();
-    }
-
-    @VisibleForTesting
-    void updateButtonSurfacePosition() {
-        if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) {
-            return;
-        }
-        final SurfaceControl leash = mButtonWindowManager.getSurfaceControl();
-
-        // Use stable bounds to prevent the button from overlapping with system bars.
-        final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
-        final Rect stableBounds = new Rect();
-        mDisplayLayout.getStableBounds(stableBounds);
-        stableBounds.intersect(taskBounds);
-
-        // Position of the button in the container coordinate.
-        final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
-                ? stableBounds.left - taskBounds.left
-                : stableBounds.right - taskBounds.left - mButtonWidth;
-        final int positionY = stableBounds.bottom - taskBounds.top - mButtonHeight;
-
-        updateSurfacePosition(leash, positionX, positionY);
-    }
-
-    @VisibleForTesting
-    void updateHintSurfacePosition() {
-        if (mHint == null || mHintWindowManager == null
-                || mHintWindowManager.getSurfaceControl() == null) {
-            return;
-        }
-        final SurfaceControl leash = mHintWindowManager.getSurfaceControl();
-
-        // Use stable bounds to prevent the hint from overlapping with system bars.
-        final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
-        final Rect stableBounds = new Rect();
-        mDisplayLayout.getStableBounds(stableBounds);
-        stableBounds.intersect(taskBounds);
-
-        // Position of the hint in the container coordinate.
-        final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
-                ? stableBounds.left - taskBounds.left + mPopupOffsetX
-                : stableBounds.right - taskBounds.left - mPopupOffsetX - mHint.getMeasuredWidth();
-        final int positionY =
-                stableBounds.bottom - taskBounds.top - mPopupOffsetY - mHint.getMeasuredHeight();
-
-        updateSurfacePosition(leash, positionX, positionY);
-    }
-
-    private void updateSurfacePosition(SurfaceControl leash, int positionX, int positionY) {
-        mSyncQueue.runInSync(t -> {
-            if (!leash.isValid()) {
-                Log.w(TAG, "The leash has been released.");
-                return;
-            }
-            t.setPosition(leash, positionX, positionY);
-            // The size compat UI should be the topmost child of the Task in case there can be more
-            // than one children.
-            t.setLayer(leash, Integer.MAX_VALUE);
-        });
-    }
-
-    int getDisplayId() {
-        return mDisplayId;
-    }
-
-    int getTaskId() {
-        return mTaskId;
-    }
-
-    private int getLayoutDirection() {
-        return mContext.getResources().getConfiguration().getLayoutDirection();
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
deleted file mode 100644
index 82f69c3..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.view.IWindow;
-import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
-import android.view.View;
-import android.view.WindowlessWindowManager;
-
-import com.android.wm.shell.R;
-
-/**
- * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton} or
- * {@link SizeCompatHintPopup}.
- */
-class SizeCompatUIWindowManager extends WindowlessWindowManager {
-
-    private Context mContext;
-    private final SizeCompatUILayout mLayout;
-
-    @Nullable
-    private SurfaceControlViewHost mViewHost;
-    @Nullable
-    private SurfaceControl mLeash;
-
-    SizeCompatUIWindowManager(Context context, Configuration config, SizeCompatUILayout layout) {
-        super(config, null /* rootSurface */, null /* hostInputToken */);
-        mContext = context;
-        mLayout = layout;
-    }
-
-    @Override
-    public void setConfiguration(Configuration configuration) {
-        super.setConfiguration(configuration);
-        mContext = mContext.createConfigurationContext(configuration);
-    }
-
-    @Override
-    protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
-        // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
-        final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
-                .setContainerLayer()
-                .setName("SizeCompatUILeash")
-                .setHidden(false)
-                .setCallsite("SizeCompatUIWindowManager#attachToParentSurface");
-        mLayout.attachToParentSurface(builder);
-        mLeash = builder.build();
-        b.setParent(mLeash);
-    }
-
-    /** Inflates {@link SizeCompatRestartButton} on to the root surface. */
-    SizeCompatRestartButton createSizeCompatButton() {
-        if (mViewHost != null) {
-            throw new IllegalStateException(
-                    "A UI has already been created with this window manager.");
-        }
-
-        mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
-
-        final SizeCompatRestartButton button = (SizeCompatRestartButton)
-                LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null);
-        button.inject(mLayout);
-        mViewHost.setView(button, mLayout.getButtonWindowLayoutParams());
-        return button;
-    }
-
-    /** Inflates {@link SizeCompatHintPopup} on to the root surface. */
-    SizeCompatHintPopup createSizeCompatHint() {
-        if (mViewHost != null) {
-            throw new IllegalStateException(
-                    "A UI has already been created with this window manager.");
-        }
-
-        mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
-
-        final SizeCompatHintPopup hint = (SizeCompatHintPopup)
-                LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null);
-        // Measure how big the hint is.
-        hint.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
-        hint.inject(mLayout);
-        mViewHost.setView(hint, mLayout.getHintWindowLayoutParams(hint));
-        return hint;
-    }
-
-    /** Releases the surface control and tears down the view hierarchy. */
-    void release() {
-        if (mViewHost != null) {
-            mViewHost.release();
-            mViewHost = null;
-        }
-
-        if (mLeash != null) {
-            final SurfaceControl leash = mLeash;
-            mLayout.mSyncQueue.runInSync(t -> t.remove(leash));
-            mLeash = null;
-        }
-    }
-
-    /**
-     * Gets {@link SurfaceControl} of the surface holding size compat UI view. @return {@code null}
-     * if not feasible.
-     */
-    @Nullable
-    SurfaceControl getSurfaceControl() {
-        return mLeash;
-    }
-}
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 05552aa..46c4a40 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
@@ -102,6 +102,7 @@
     static final int EXIT_REASON_ROOT_TASK_VANISHED = 6;
     static final int EXIT_REASON_SCREEN_LOCKED = 7;
     static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8;
+    static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
     @IntDef(value = {
             EXIT_REASON_UNKNOWN,
             EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
@@ -112,6 +113,7 @@
             EXIT_REASON_ROOT_TASK_VANISHED,
             EXIT_REASON_SCREEN_LOCKED,
             EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP,
+            EXIT_REASON_CHILD_TASK_ENTER_PIP,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface ExitReason{}
@@ -184,6 +186,15 @@
         return mStageCoordinator.isSplitScreenVisible();
     }
 
+    @Nullable
+    public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
+        if (isSplitScreenVisible()) {
+            int taskId = mStageCoordinator.getTaskId(splitPosition);
+            return mTaskOrganizer.getRunningTaskInfo(taskId);
+        }
+        return null;
+    }
+
     public boolean isTaskInSplitScreen(int taskId) {
         return isSplitScreenVisible()
                 && mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
@@ -406,6 +417,8 @@
                 return "APP_FINISHED";
             case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW:
                 return "APP_DOES_NOT_SUPPORT_MULTIWINDOW";
+            case EXIT_REASON_CHILD_TASK_ENTER_PIP:
+                return "CHILD_TASK_ENTER_PIP";
             default:
                 return "unknown reason, reason int = " + exitReason;
         }
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 e30e6c5..da78d5e 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
@@ -35,6 +35,7 @@
 import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -521,6 +522,14 @@
         return SplitLayout.reversePosition(mSideStagePosition);
     }
 
+    int getTaskId(@SplitPosition int splitPosition) {
+        if (mSideStagePosition == splitPosition) {
+            return mSideStage.getTopVisibleChildTaskId();
+        } else {
+            return mMainStage.getTopVisibleChildTaskId();
+        }
+    }
+
     void setSideStagePosition(@SplitPosition int sideStagePosition,
             @Nullable WindowContainerTransaction wct) {
         setSideStagePosition(sideStagePosition, true /* updateBounds */, wct);
@@ -629,8 +638,12 @@
         });
         mShouldUpdateRecents = false;
 
-        mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
-        mMainStage.deactivate(wct, childrenToTop == mMainStage);
+        // When the exit split-screen is caused by one of the task enters auto pip,
+        // we want the tasks to be put to bottom instead of top, otherwise it will end up
+        // a fullscreen plus a pinned task instead of pinned only at the end of the transition.
+        final boolean fromEnteringPip = exitReason == EXIT_REASON_CHILD_TASK_ENTER_PIP;
+        mSideStage.removeAllTasks(wct, !fromEnteringPip && childrenToTop == mSideStage);
+        mMainStage.deactivate(wct, !fromEnteringPip && childrenToTop == mMainStage);
         mTaskOrganizer.applyTransaction(wct);
         mSyncQueue.runInSync(t -> t
                 .setWindowCrop(mMainStage.mRootLeash, null)
@@ -660,6 +673,8 @@
             case EXIT_REASON_DRAG_DIVIDER:
             // Either of the split apps have finished
             case EXIT_REASON_APP_FINISHED:
+            // One of the children enters PiP
+            case EXIT_REASON_CHILD_TASK_ENTER_PIP:
                 return true;
             default:
                 return false;
@@ -749,6 +764,11 @@
         }
     }
 
+    private void onStageChildTaskEnterPip(StageListenerImpl stageListener, int taskId) {
+        exitSplitScreen(stageListener == mMainStageListener ? mMainStage : mSideStage,
+                EXIT_REASON_CHILD_TASK_ENTER_PIP);
+    }
+
     private void updateRecentTasksSplitPair() {
         if (!mShouldUpdateRecents) {
             return;
@@ -1437,6 +1457,11 @@
         }
 
         @Override
+        public void onChildTaskEnterPip(int taskId) {
+            StageCoordinator.this.onStageChildTaskEnterPip(this, taskId);
+        }
+
+        @Override
         public void onRootTaskVanished() {
             reset();
             StageCoordinator.this.onStageRootTaskVanished(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index cd10b9f..2c853c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -73,6 +74,8 @@
 
         void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
 
+        void onChildTaskEnterPip(int taskId);
+
         void onRootTaskVanished();
 
         void onNoLongerSupportMultiWindow();
@@ -256,6 +259,9 @@
             mChildrenTaskInfo.remove(taskId);
             mChildrenLeashes.remove(taskId);
             mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
+            if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
+                mCallbacks.onChildTaskEnterPip(taskId);
+            }
             if (ENABLE_SHELL_TRANSITIONS) {
                 // Status is managed/synchronized by the transition lifecycle.
                 return;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 1fcbf14..a3b98a8f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -56,7 +56,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.sizecompatui.SizeCompatUIController;
+import com.android.wm.shell.compatui.CompatUIController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -82,7 +82,7 @@
     @Mock
     private Context mContext;
     @Mock
-    private SizeCompatUIController mSizeCompatUI;
+    private CompatUIController mCompatUI;
 
     ShellTaskOrganizer mOrganizer;
     private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class);
@@ -132,7 +132,7 @@
                     .when(mTaskOrganizerController).registerTaskOrganizer(any());
         } catch (RemoteException e) {}
         mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
-                mSizeCompatUI, Optional.empty()));
+                mCompatUI, Optional.empty()));
     }
 
     @Test
@@ -334,34 +334,34 @@
         mOrganizer.onTaskAppeared(taskInfo1, null);
 
         // sizeCompatActivity is null if top activity is not in size compat.
-        verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+        verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
                 null /* taskConfig */, null /* taskListener */);
 
         // sizeCompatActivity is non-null if top activity is in size compat.
-        clearInvocations(mSizeCompatUI);
+        clearInvocations(mCompatUI);
         final RunningTaskInfo taskInfo2 =
                 createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
         taskInfo2.displayId = taskInfo1.displayId;
         taskInfo2.topActivityInSizeCompat = true;
         taskInfo2.isVisible = true;
         mOrganizer.onTaskInfoChanged(taskInfo2);
-        verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+        verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
                 taskInfo1.configuration, taskListener);
 
         // Not show size compat UI if task is not visible.
-        clearInvocations(mSizeCompatUI);
+        clearInvocations(mCompatUI);
         final RunningTaskInfo taskInfo3 =
                 createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
         taskInfo3.displayId = taskInfo1.displayId;
         taskInfo3.topActivityInSizeCompat = true;
         taskInfo3.isVisible = false;
         mOrganizer.onTaskInfoChanged(taskInfo3);
-        verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+        verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
                 null /* taskConfig */, null /* taskListener */);
 
-        clearInvocations(mSizeCompatUI);
+        clearInvocations(mCompatUI);
         mOrganizer.onTaskVanished(taskInfo1);
-        verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
+        verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
                 null /* taskConfig */, null /* taskListener */);
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index bc701d0..8bc1223 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -39,6 +39,7 @@
 import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.Log;
 import android.util.Pair;
 import android.view.WindowManager;
 
@@ -913,6 +914,31 @@
         assertSelectionChangedTo(mBubbleA2);
     }
 
+    /**
+      * - have a maxed out bubble stack & all of the bubbles have been recently accessed
+      * - bubble a notification that was posted before any of those bubbles were accessed
+      * => that bubble should be added
+     *
+      */
+    @Test
+    public void test_addOldNotifWithNewerBubbles() {
+        sendUpdatedEntryAtTime(mEntryA1, 2000);
+        sendUpdatedEntryAtTime(mEntryA2, 3000);
+        sendUpdatedEntryAtTime(mEntryA3, 4000);
+        sendUpdatedEntryAtTime(mEntryB1, 5000);
+        sendUpdatedEntryAtTime(mEntryB2, 6000);
+
+        mBubbleData.setListener(mListener);
+        sendUpdatedEntryAtTime(mEntryB3, 1000 /* postTime */, 7000 /* currentTime */);
+        verifyUpdateReceived();
+
+        // B3 is in the stack
+        assertThat(mBubbleData.getBubbleInStackWithKey(mBubbleB3.getKey())).isNotNull();
+        // A1 is the oldest so it's in the overflow
+        assertThat(mBubbleData.getOverflowBubbleWithKey(mEntryA1.getKey())).isNotNull();
+        assertOrderChangedTo(mBubbleB3, mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA2);
+    }
+
     private void verifyUpdateReceived() {
         verify(mListener).applyUpdate(mUpdateCaptor.capture());
         reset(mListener);
@@ -1014,6 +1040,12 @@
     }
 
     private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime) {
+        setCurrentTime(postTime);
+        sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */);
+    }
+
+    private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime, long currentTime) {
+        setCurrentTime(currentTime);
         sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */);
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
similarity index 83%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 877b192..f622edb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.sizecompatui;
+package com.android.wm.shell.compatui;
 
 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 
@@ -56,18 +56,18 @@
 import org.mockito.MockitoAnnotations;
 
 /**
- * Tests for {@link SizeCompatUIController}.
+ * Tests for {@link CompatUIController}.
  *
  * Build/Install/Run:
- *  atest WMShellUnitTests:SizeCompatUIControllerTest
+ *  atest WMShellUnitTests:CompatUIControllerTest
  */
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
-public class SizeCompatUIControllerTest extends ShellTestCase {
+public class CompatUIControllerTest extends ShellTestCase {
     private static final int DISPLAY_ID = 0;
     private static final int TASK_ID = 12;
 
-    private SizeCompatUIController mController;
+    private CompatUIController mController;
     private @Mock DisplayController mMockDisplayController;
     private @Mock DisplayInsetsController mMockDisplayInsetsController;
     private @Mock DisplayLayout mMockDisplayLayout;
@@ -75,7 +75,7 @@
     private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
     private @Mock SyncTransactionQueue mMockSyncQueue;
     private @Mock ShellExecutor mMockExecutor;
-    private @Mock SizeCompatUILayout mMockLayout;
+    private @Mock CompatUIWindowManager mMockLayout;
 
     @Captor
     ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -87,10 +87,10 @@
         doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt());
         doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
         doReturn(TASK_ID).when(mMockLayout).getTaskId();
-        mController = new SizeCompatUIController(mContext, mMockDisplayController,
+        mController = new CompatUIController(mContext, mMockDisplayController,
                 mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
             @Override
-            SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
+            CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
                     Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
                 return mMockLayout;
             }
@@ -105,24 +105,24 @@
     }
 
     @Test
-    public void testOnSizeCompatInfoChanged() {
+    public void testOnCompatInfoChanged() {
         final Configuration taskConfig = new Configuration();
 
         // Verify that the restart button is added with non-null size compat info.
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
         verify(mController).createLayout(any(), eq(DISPLAY_ID), eq(TASK_ID), eq(taskConfig),
                 eq(mMockTaskListener));
 
         // Verify that the restart button is updated with non-null new size compat info.
         final Configuration newTaskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener);
 
-        verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
+        verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
                 true /* show */);
 
         // Verify that the restart button is removed with null size compat info.
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener);
 
         verify(mMockLayout).release();
     }
@@ -140,7 +140,7 @@
     public void testOnDisplayRemoved() {
         mController.onDisplayAdded(DISPLAY_ID);
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
                 mMockTaskListener);
 
         mController.onDisplayRemoved(DISPLAY_ID + 1);
@@ -158,7 +158,7 @@
     @Test
     public void testOnDisplayConfigurationChanged() {
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
                 mMockTaskListener);
 
         final Configuration newTaskConfig = new Configuration();
@@ -175,7 +175,7 @@
     public void testInsetsChanged() {
         mController.onDisplayAdded(DISPLAY_ID);
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
                 mMockTaskListener);
         InsetsState insetsState = new InsetsState();
         InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
@@ -197,7 +197,7 @@
     @Test
     public void testChangeButtonVisibilityOnImeShowHide() {
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
         // Verify that the restart button is hidden after IME is showing.
         mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
@@ -205,9 +205,9 @@
         verify(mMockLayout).updateVisibility(false);
 
         // Verify button remains hidden while IME is showing.
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
-        verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
+        verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
                 false /* show */);
 
         // Verify button is shown after IME is hidden.
@@ -219,7 +219,7 @@
     @Test
     public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
         // Verify that the restart button is hidden after keyguard becomes occluded.
         mController.onKeyguardOccludedChanged(true);
@@ -227,9 +227,9 @@
         verify(mMockLayout).updateVisibility(false);
 
         // Verify button remains hidden while keyguard is occluded.
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
-        verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
+        verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
                 false /* show */);
 
         // Verify button is shown after keyguard becomes not occluded.
@@ -241,7 +241,7 @@
     @Test
     public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
         mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
         mController.onKeyguardOccludedChanged(true);
@@ -264,7 +264,7 @@
     @Test
     public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
         final Configuration taskConfig = new Configuration();
-        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+        mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
 
         mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
         mController.onKeyguardOccludedChanged(true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
new file mode 100644
index 0000000..2c3987b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+import android.view.SurfaceControlViewHost;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link CompatUILayout}.
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:CompatUILayoutTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class CompatUILayoutTest extends ShellTestCase {
+
+    private static final int TASK_ID = 1;
+
+    @Mock private SyncTransactionQueue mSyncTransactionQueue;
+    @Mock private CompatUIController.CompatUICallback mCallback;
+    @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+    @Mock private SurfaceControlViewHost mViewHost;
+
+    private CompatUIWindowManager mWindowManager;
+    private CompatUILayout mCompatUILayout;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
+                mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
+                false /* hasShownHint */);
+
+        mCompatUILayout = (CompatUILayout)
+                LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
+        mCompatUILayout.inject(mWindowManager);
+
+        spyOn(mWindowManager);
+        spyOn(mCompatUILayout);
+        doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
+    }
+
+    @Test
+    public void testOnClickForRestartButton() {
+        final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button);
+        button.performClick();
+
+        verify(mWindowManager).onRestartButtonClicked();
+        doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
+        verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+    }
+
+    @Test
+    public void testOnLongClickForRestartButton() {
+        doNothing().when(mWindowManager).onRestartButtonLongClicked();
+
+        final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button);
+        button.performLongClick();
+
+        verify(mWindowManager).onRestartButtonLongClicked();
+    }
+
+    @Test
+    public void testOnClickForSizeCompatHint() {
+        mWindowManager.createLayout(true /* show */);
+        final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint);
+        sizeCompatHint.performClick();
+
+        verify(mCompatUILayout).setSizeCompatHintVisibility(/* show= */ false);
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
new file mode 100644
index 0000000..d5dcf2e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui;
+
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.view.DisplayInfo;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link CompatUIWindowManager}.
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:CompatUIWindowManagerTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class CompatUIWindowManagerTest extends ShellTestCase {
+
+    private static final int TASK_ID = 1;
+
+    @Mock private SyncTransactionQueue mSyncTransactionQueue;
+    @Mock private CompatUIController.CompatUICallback mCallback;
+    @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+    @Mock private CompatUILayout mCompatUILayout;
+    @Mock private SurfaceControlViewHost mViewHost;
+    private Configuration mTaskConfig;
+
+    private CompatUIWindowManager mWindowManager;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mTaskConfig = new Configuration();
+
+        mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
+                mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
+                false /* hasShownHint */);
+
+        spyOn(mWindowManager);
+        doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
+        doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
+    }
+
+    @Test
+    public void testCreateSizeCompatButton() {
+        // Not create layout if show is false.
+        mWindowManager.createLayout(false /* show */);
+
+        verify(mWindowManager, never()).inflateCompatUILayout();
+
+        // Not create hint popup.
+        mWindowManager.mShouldShowHint = false;
+        mWindowManager.createLayout(true /* show */);
+
+        verify(mWindowManager).inflateCompatUILayout();
+        verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
+
+        // Create hint popup.
+        mWindowManager.release();
+        mWindowManager.mShouldShowHint = true;
+        mWindowManager.createLayout(true /* show */);
+
+        verify(mWindowManager, times(2)).inflateCompatUILayout();
+        assertNotNull(mCompatUILayout);
+        verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
+        assertFalse(mWindowManager.mShouldShowHint);
+    }
+
+    @Test
+    public void testRelease() {
+        mWindowManager.createLayout(true /* show */);
+
+        verify(mWindowManager).inflateCompatUILayout();
+
+        mWindowManager.release();
+
+        verify(mViewHost).release();
+    }
+
+    @Test
+    public void testUpdateCompatInfo() {
+        mWindowManager.createLayout(true /* show */);
+
+        // No diff
+        clearInvocations(mWindowManager);
+        mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */);
+
+        verify(mWindowManager, never()).updateSurfacePosition();
+        verify(mWindowManager, never()).release();
+        verify(mWindowManager, never()).createLayout(anyBoolean());
+
+        // Change task listener, recreate button.
+        clearInvocations(mWindowManager);
+        final ShellTaskOrganizer.TaskListener newTaskListener = mock(
+                ShellTaskOrganizer.TaskListener.class);
+        mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
+                true /* show */);
+
+        verify(mWindowManager).release();
+        verify(mWindowManager).createLayout(anyBoolean());
+
+        // Change task bounds, update position.
+        clearInvocations(mWindowManager);
+        final Configuration newTaskConfiguration = new Configuration();
+        newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
+        mWindowManager.updateCompatInfo(newTaskConfiguration, newTaskListener,
+                true /* show */);
+
+        verify(mWindowManager).updateSurfacePosition();
+    }
+
+    @Test
+    public void testUpdateDisplayLayout() {
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = 1000;
+        displayInfo.logicalHeight = 2000;
+        final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
+                mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
+
+        mWindowManager.updateDisplayLayout(displayLayout1);
+        verify(mWindowManager).updateSurfacePosition();
+
+        // No update if the display bounds is the same.
+        clearInvocations(mWindowManager);
+        final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
+                mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
+        mWindowManager.updateDisplayLayout(displayLayout2);
+        verify(mWindowManager, never()).updateSurfacePosition();
+    }
+
+    @Test
+    public void testUpdateDisplayLayoutInsets() {
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = 1000;
+        displayInfo.logicalHeight = 2000;
+        final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
+                mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
+
+        mWindowManager.updateDisplayLayout(displayLayout);
+        verify(mWindowManager).updateSurfacePosition();
+
+        // Update if the insets change on the existing display layout
+        clearInvocations(mWindowManager);
+        InsetsState insetsState = new InsetsState();
+        InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
+        insetsSource.setFrame(0, 0, 1000, 1000);
+        insetsState.addSource(insetsSource);
+        displayLayout.setInsets(mContext.getResources(), insetsState);
+        mWindowManager.updateDisplayLayout(displayLayout);
+        verify(mWindowManager).updateSurfacePosition();
+    }
+
+    @Test
+    public void testUpdateVisibility() {
+        // Create button if it is not created.
+        mWindowManager.mCompatUILayout = null;
+        mWindowManager.updateVisibility(true /* show */);
+
+        verify(mWindowManager).createLayout(true /* show */);
+
+        // Hide button.
+        clearInvocations(mWindowManager);
+        doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility();
+        mWindowManager.updateVisibility(false /* show */);
+
+        verify(mWindowManager, never()).createLayout(anyBoolean());
+        verify(mCompatUILayout).setVisibility(View.GONE);
+
+        // Show button.
+        doReturn(View.GONE).when(mCompatUILayout).getVisibility();
+        mWindowManager.updateVisibility(true /* show */);
+
+        verify(mWindowManager, never()).createLayout(anyBoolean());
+        verify(mCompatUILayout).setVisibility(View.VISIBLE);
+    }
+
+    @Test
+    public void testAttachToParentSurface() {
+        final SurfaceControl.Builder b = new SurfaceControl.Builder();
+        mWindowManager.attachToParentSurface(b);
+
+        verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b);
+    }
+
+    @Test
+    public void testOnRestartButtonClicked() {
+        mWindowManager.onRestartButtonClicked();
+
+        verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
+    }
+
+    @Test
+    public void testOnRestartButtonLongClicked_showHint() {
+       // Not create hint popup.
+        mWindowManager.mShouldShowHint = false;
+        mWindowManager.createLayout(true /* show */);
+
+        verify(mWindowManager).inflateCompatUILayout();
+        verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
+
+        mWindowManager.onRestartButtonLongClicked();
+
+        verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
+    }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index a6215d3..8e30f65 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -188,7 +188,7 @@
         final Rect newBounds = new Rect(50, 50, 100, 75);
         mPipBoundsState.setBounds(currentBounds);
 
-        mPipBoundsState.setPipExclusionBoundsChangeCallback(callback);
+        mPipBoundsState.addPipExclusionBoundsChangeCallback(callback);
         // Setting the listener immediately calls back with the current bounds.
         verify(callback).accept(currentBounds);
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
deleted file mode 100644
index 3a14a33..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.testing.AndroidTestingRunner;
-import android.view.LayoutInflater;
-import android.widget.LinearLayout;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link SizeCompatHintPopup}.
- *
- * Build/Install/Run:
- *  atest WMShellUnitTests:SizeCompatHintPopupTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatHintPopupTest extends ShellTestCase {
-
-    @Mock private SyncTransactionQueue mSyncTransactionQueue;
-    @Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
-    @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
-    @Mock private DisplayLayout mDisplayLayout;
-
-    private SizeCompatUILayout mLayout;
-    private SizeCompatHintPopup mHint;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        final int taskId = 1;
-        mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
-                new Configuration(), taskId, mTaskListener, mDisplayLayout,
-                false /* hasShownHint */);
-        mHint = (SizeCompatHintPopup)
-                LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null);
-        mHint.inject(mLayout);
-
-        spyOn(mLayout);
-    }
-
-    @Test
-    public void testOnClick() {
-        doNothing().when(mLayout).dismissHint();
-
-        final LinearLayout hintPopup = mHint.findViewById(R.id.size_compat_hint_popup);
-        hintPopup.performClick();
-
-        verify(mLayout).dismissHint();
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
deleted file mode 100644
index a20a5e9..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.testing.AndroidTestingRunner;
-import android.view.LayoutInflater;
-import android.widget.ImageButton;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link SizeCompatRestartButton}.
- *
- * Build/Install/Run:
- *  atest WMShellUnitTests:SizeCompatRestartButtonTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatRestartButtonTest extends ShellTestCase {
-
-    private static final int TASK_ID = 1;
-
-    @Mock private SyncTransactionQueue mSyncTransactionQueue;
-    @Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
-    @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
-    @Mock private DisplayLayout mDisplayLayout;
-
-    private SizeCompatUILayout mLayout;
-    private SizeCompatRestartButton mButton;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
-                new Configuration(), TASK_ID, mTaskListener, mDisplayLayout,
-                false /* hasShownHint */);
-        mButton = (SizeCompatRestartButton)
-                LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null);
-        mButton.inject(mLayout);
-
-        spyOn(mLayout);
-    }
-
-    @Test
-    public void testOnClick() {
-        final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
-        button.performClick();
-
-        verify(mLayout).onRestartButtonClicked();
-        verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
-    }
-
-    @Test
-    public void testOnLongClick() {
-        doNothing().when(mLayout).onRestartButtonLongClicked();
-
-        final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
-        button.performLongClick();
-
-        verify(mLayout).onRestartButtonLongClicked();
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
deleted file mode 100644
index eb9305b..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.sizecompatui;
-
-import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.testing.AndroidTestingRunner;
-import android.view.DisplayInfo;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.SurfaceControl;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link SizeCompatUILayout}.
- *
- * Build/Install/Run:
- *  atest WMShellUnitTests:SizeCompatUILayoutTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatUILayoutTest extends ShellTestCase {
-
-    private static final int TASK_ID = 1;
-
-    @Mock private SyncTransactionQueue mSyncTransactionQueue;
-    @Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
-    @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
-    @Mock private DisplayLayout mDisplayLayout;
-    @Mock private SizeCompatRestartButton mButton;
-    @Mock private SizeCompatHintPopup mHint;
-    private Configuration mTaskConfig;
-
-    private SizeCompatUILayout mLayout;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mTaskConfig = new Configuration();
-
-        mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
-                new Configuration(), TASK_ID, mTaskListener, new DisplayLayout(),
-                false /* hasShownHint */);
-
-        spyOn(mLayout);
-        spyOn(mLayout.mButtonWindowManager);
-        doReturn(mButton).when(mLayout.mButtonWindowManager).createSizeCompatButton();
-
-        final SizeCompatUIWindowManager hintWindowManager = mLayout.createHintWindowManager();
-        spyOn(hintWindowManager);
-        doReturn(mHint).when(hintWindowManager).createSizeCompatHint();
-        doReturn(hintWindowManager).when(mLayout).createHintWindowManager();
-    }
-
-    @Test
-    public void testCreateSizeCompatButton() {
-        // Not create button if show is false.
-        mLayout.createSizeCompatButton(false /* show */);
-
-        verify(mLayout.mButtonWindowManager, never()).createSizeCompatButton();
-        assertNull(mLayout.mButton);
-        assertNull(mLayout.mHintWindowManager);
-        assertNull(mLayout.mHint);
-
-        // Not create hint popup.
-        mLayout.mShouldShowHint = false;
-        mLayout.createSizeCompatButton(true /* show */);
-
-        verify(mLayout.mButtonWindowManager).createSizeCompatButton();
-        assertNotNull(mLayout.mButton);
-        assertNull(mLayout.mHintWindowManager);
-        assertNull(mLayout.mHint);
-
-        // Create hint popup.
-        mLayout.release();
-        mLayout.mShouldShowHint = true;
-        mLayout.createSizeCompatButton(true /* show */);
-
-        verify(mLayout.mButtonWindowManager, times(2)).createSizeCompatButton();
-        assertNotNull(mLayout.mButton);
-        assertNotNull(mLayout.mHintWindowManager);
-        verify(mLayout.mHintWindowManager).createSizeCompatHint();
-        assertNotNull(mLayout.mHint);
-        assertFalse(mLayout.mShouldShowHint);
-    }
-
-    @Test
-    public void testRelease() {
-        mLayout.createSizeCompatButton(true /* show */);
-        final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager;
-
-        mLayout.release();
-
-        assertNull(mLayout.mButton);
-        assertNull(mLayout.mHint);
-        verify(hintWindowManager).release();
-        assertNull(mLayout.mHintWindowManager);
-        verify(mLayout.mButtonWindowManager).release();
-    }
-
-    @Test
-    public void testUpdateSizeCompatInfo() {
-        mLayout.createSizeCompatButton(true /* show */);
-
-        // No diff
-        clearInvocations(mLayout);
-        mLayout.updateSizeCompatInfo(mTaskConfig, mTaskListener, true /* show */);
-
-        verify(mLayout, never()).updateButtonSurfacePosition();
-        verify(mLayout, never()).release();
-        verify(mLayout, never()).createSizeCompatButton(anyBoolean());
-
-        // Change task listener, recreate button.
-        clearInvocations(mLayout);
-        final ShellTaskOrganizer.TaskListener newTaskListener = mock(
-                ShellTaskOrganizer.TaskListener.class);
-        mLayout.updateSizeCompatInfo(mTaskConfig, newTaskListener,
-                true /* show */);
-
-        verify(mLayout).release();
-        verify(mLayout).createSizeCompatButton(anyBoolean());
-
-        // Change task bounds, update position.
-        clearInvocations(mLayout);
-        final Configuration newTaskConfiguration = new Configuration();
-        newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
-        mLayout.updateSizeCompatInfo(newTaskConfiguration, newTaskListener,
-                true /* show */);
-
-        verify(mLayout).updateButtonSurfacePosition();
-        verify(mLayout).updateHintSurfacePosition();
-    }
-
-    @Test
-    public void testUpdateDisplayLayout() {
-        final DisplayInfo displayInfo = new DisplayInfo();
-        displayInfo.logicalWidth = 1000;
-        displayInfo.logicalHeight = 2000;
-        final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
-                mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
-
-        mLayout.updateDisplayLayout(displayLayout1);
-        verify(mLayout).updateButtonSurfacePosition();
-        verify(mLayout).updateHintSurfacePosition();
-
-        // No update if the display bounds is the same.
-        clearInvocations(mLayout);
-        final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
-                mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
-        mLayout.updateDisplayLayout(displayLayout2);
-        verify(mLayout, never()).updateButtonSurfacePosition();
-        verify(mLayout, never()).updateHintSurfacePosition();
-    }
-
-    @Test
-    public void testUpdateDisplayLayoutInsets() {
-        final DisplayInfo displayInfo = new DisplayInfo();
-        displayInfo.logicalWidth = 1000;
-        displayInfo.logicalHeight = 2000;
-        final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
-                mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
-
-        mLayout.updateDisplayLayout(displayLayout);
-        verify(mLayout).updateButtonSurfacePosition();
-        verify(mLayout).updateHintSurfacePosition();
-
-        // Update if the insets change on the existing display layout
-        clearInvocations(mLayout);
-        InsetsState insetsState = new InsetsState();
-        InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
-        insetsSource.setFrame(0, 0, 1000, 1000);
-        insetsState.addSource(insetsSource);
-        displayLayout.setInsets(mContext.getResources(), insetsState);
-        mLayout.updateDisplayLayout(displayLayout);
-        verify(mLayout).updateButtonSurfacePosition();
-        verify(mLayout).updateHintSurfacePosition();
-    }
-
-    @Test
-    public void testUpdateVisibility() {
-        // Create button if it is not created.
-        mLayout.mButton = null;
-        mLayout.updateVisibility(true /* show */);
-
-        verify(mLayout).createSizeCompatButton(true /* show */);
-
-        // Hide button.
-        clearInvocations(mLayout);
-        doReturn(View.VISIBLE).when(mButton).getVisibility();
-        mLayout.updateVisibility(false /* show */);
-
-        verify(mLayout, never()).createSizeCompatButton(anyBoolean());
-        verify(mButton).setVisibility(View.GONE);
-
-        // Show button.
-        doReturn(View.GONE).when(mButton).getVisibility();
-        mLayout.updateVisibility(true /* show */);
-
-        verify(mLayout, never()).createSizeCompatButton(anyBoolean());
-        verify(mButton).setVisibility(View.VISIBLE);
-    }
-
-    @Test
-    public void testAttachToParentSurface() {
-        final SurfaceControl.Builder b = new SurfaceControl.Builder();
-        mLayout.attachToParentSurface(b);
-
-        verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b);
-    }
-
-    @Test
-    public void testOnRestartButtonClicked() {
-        mLayout.onRestartButtonClicked();
-
-        verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
-    }
-
-    @Test
-    public void testOnRestartButtonLongClicked_showHint() {
-        mLayout.dismissHint();
-
-        assertNull(mLayout.mHint);
-
-        mLayout.onRestartButtonLongClicked();
-
-        assertNotNull(mLayout.mHint);
-    }
-
-    @Test
-    public void testDismissHint() {
-        mLayout.onRestartButtonLongClicked();
-        final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager;
-        assertNotNull(mLayout.mHint);
-        assertNotNull(hintWindowManager);
-
-        mLayout.dismissHint();
-
-        assertNull(mLayout.mHint);
-        assertNull(mLayout.mHintWindowManager);
-        verify(hintWindowManager).release();
-    }
-}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 446e580..13aff38 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -695,6 +695,12 @@
         std::unordered_set<uint32_t> finalized_ids;
         const auto lib_alias = child_chunk.header<ResTable_staged_alias_header>();
         if (!lib_alias) {
+          LOG(ERROR) << "RES_TABLE_STAGED_ALIAS_TYPE is too small.";
+          return {};
+        }
+        if ((child_chunk.data_size() / sizeof(ResTable_staged_alias_entry))
+            < dtohl(lib_alias->count)) {
+          LOG(ERROR) << "RES_TABLE_STAGED_ALIAS_TYPE is too small to hold entries.";
           return {};
         }
         const auto entry_begin = child_chunk.data_ptr().convert<ResTable_staged_alias_entry>();
diff --git a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
index 36c2bda..7f17d26 100644
--- a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
+++ b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ResolveInfo;
 
 import com.android.settingslib.utils.BuildCompatUtils;
 
@@ -37,11 +36,10 @@
         if (BuildCompatUtils.isAtLeastS()) {
             final Intent intent = new Intent(ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY);
             intent.setPackage(PACKAGE_NAME_SETTINGS);
-            final ResolveInfo resolveInfo =
-                    context.getPackageManager().resolveActivity(intent, 0 /* flags */);
-            return resolveInfo != null
-                    && resolveInfo.activityInfo != null
-                    && resolveInfo.activityInfo.enabled;
+            final boolean isEmbeddingActivityEnabled =
+                    intent.resolveActivity(context.getPackageManager()) != null;
+
+            return isEmbeddingActivityEnabled;
         }
         return false;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index 8b17be1..dee6894 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -85,6 +85,7 @@
     @VisibleForTesting
     protected Context mContext;
     private final int mThemeResId;
+    private final boolean mCancelIsNeutral;
     @VisibleForTesting
     protected TextView mZenAlarmWarning;
     @VisibleForTesting
@@ -101,8 +102,13 @@
     }
 
     public EnableZenModeDialog(Context context, int themeResId) {
+        this(context, themeResId, false /* cancelIsNeutral */);
+    }
+
+    public EnableZenModeDialog(Context context, int themeResId, boolean cancelIsNeutral) {
         mContext = context;
         mThemeResId = themeResId;
+        mCancelIsNeutral = cancelIsNeutral;
     }
 
     public AlertDialog createDialog() {
@@ -115,7 +121,6 @@
 
         final AlertDialog.Builder builder = new AlertDialog.Builder(mContext, mThemeResId)
                 .setTitle(R.string.zen_mode_settings_turn_on_dialog_title)
-                .setNegativeButton(R.string.cancel, null)
                 .setPositiveButton(R.string.zen_mode_enable_dialog_turn_on,
                         new DialogInterface.OnClickListener() {
                             @Override
@@ -145,6 +150,12 @@
                             }
                         });
 
+        if (mCancelIsNeutral) {
+            builder.setNeutralButton(R.string.cancel, null);
+        } else {
+            builder.setNegativeButton(R.string.cancel, null);
+        }
+
         View contentView = getContentView();
         bindConditions(forever());
         builder.setView(contentView);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index db301f6..aca4fe4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -25,6 +25,7 @@
 import static android.provider.Settings.SET_ALL_RESULT_DISABLED;
 import static android.provider.Settings.SET_ALL_RESULT_FAILURE;
 import static android.provider.Settings.SET_ALL_RESULT_SUCCESS;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER;
 import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
@@ -3585,7 +3586,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 204;
+            private static final int SETTINGS_VERSION = 205;
 
             private final int mUserId;
 
@@ -5227,6 +5228,30 @@
                     currentVersion = 204;
                 }
 
+                if (currentVersion == 204) {
+                    // Version 204: Reset the
+                    // Secure#ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT as enabled
+                    // status for showing the tooltips.
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting accessibilityButtonMode = secureSettings.getSettingLocked(
+                            Secure.ACCESSIBILITY_BUTTON_MODE);
+                    if (!accessibilityButtonMode.isNull()
+                            && accessibilityButtonMode.getValue().equals(
+                            String.valueOf(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU))) {
+                        if (isGestureNavigateEnabled()
+                                && hasValueInA11yButtonTargets(secureSettings)) {
+                            secureSettings.insertSettingLocked(
+                                    Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
+                                    /* enabled */ "1",
+                                    /* tag= */ null,
+                                    /* makeDefault= */ false,
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+
+                    currentVersion = 205;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml b/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml
new file mode 100644
index 0000000..230a256
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_unlocked_aod.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+    <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125">
+        <path android:name="_R_G_L_2_G_D_0_P_0"
+              android:fillColor="#FF000000"
+              android:fillAlpha="1"
+              android:fillType="nonZero"
+              android:pathData=" M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " />
+    </group>
+    <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+        <path android:name="_R_G_L_1_G_D_0_P_0"
+              android:strokeColor="#FF000000"
+              android:strokeLineCap="round"
+              android:strokeLineJoin="round"
+              android:strokeWidth="1.5"
+              android:strokeAlpha="1"
+              android:pathData=" M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " />
+    </group>
+    <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5">
+        <path android:name="_R_G_L_0_G_D_0_P_0"
+              android:strokeColor="#FF000000"
+              android:strokeLineCap="round"
+              android:strokeLineJoin="round"
+              android:strokeWidth="1.5"
+              android:strokeAlpha="1"
+              android:pathData=" M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " />
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
index c58e2e3..b3987f1 100644
--- a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
+++ b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
@@ -50,6 +50,11 @@
         android:state_first="true"
         android:state_single="true"
         android:drawable="@drawable/ic_lock_aod" />
+    <item
+        android:id="@+id/unlocked_aod"
+        android:state_last="true"
+        android:state_single="true"
+        android:drawable="@drawable/ic_unlocked_aod" />
 
     <item
         android:id="@+id/no_icon"
@@ -79,4 +84,19 @@
         android:fromId="@id/locked"
         android:toId="@id/locked_aod"
         android:drawable="@drawable/lock_ls_to_aod" />
+
+    <transition
+        android:fromId="@id/unlocked_aod"
+        android:toId="@id/unlocked"
+        android:drawable="@drawable/unlocked_aod_to_ls" />
+
+    <transition
+        android:fromId="@id/unlocked"
+        android:toId="@id/unlocked_aod"
+        android:drawable="@drawable/unlocked_ls_to_aod" />
+
+    <transition
+        android:fromId="@id/unlocked"
+        android:toId="@id/locked_aod"
+        android:drawable="@drawable/unlocked_to_aod_lock" />
 </animated-selector>
diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml
new file mode 100644
index 0000000..3b59ba8
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlocked_aod_to_ls.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+                 xmlns:android="http://schemas.android.com/apk/res/android">
+    <aapt:attr name="android:drawable">
+        <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125">
+                    <path android:name="_R_G_L_2_G_D_0_P_0"
+                          android:fillColor="#FF000000"
+                          android:fillAlpha="1"
+                          android:fillType="nonZero"
+                          android:pathData=" M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " />
+                </group>
+                <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+                    <path android:name="_R_G_L_1_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="1.5"
+                          android:strokeAlpha="1"
+                          android:pathData=" M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " />
+                </group>
+                <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5">
+                    <path android:name="_R_G_L_0_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="1.5"
+                          android:strokeAlpha="1"
+                          android:pathData=" M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " />
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData" android:duration="333" android:startOffset="0" android:valueFrom="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueTo="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.372,0 0.203,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="1.5"
+                                android:valueTo="2"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.386,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueTo="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.372,0 0.203,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="1.5"
+                                android:valueTo="2.5"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " android:valueTo="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateX"
+                                android:duration="517"
+                                android:startOffset="0"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml
new file mode 100644
index 0000000..1c6d0b5
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlocked_ls_to_aod.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+                 xmlns:android="http://schemas.android.com/apk/res/android">
+    <aapt:attr name="android:drawable">
+        <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125">
+                    <path android:name="_R_G_L_2_G_D_0_P_0"
+                          android:fillColor="#FF000000"
+                          android:fillAlpha="1"
+                          android:fillType="nonZero"
+                          android:pathData=" M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " />
+                </group>
+                <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+                    <path android:name="_R_G_L_1_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="2"
+                          android:strokeAlpha="1"
+                          android:pathData=" M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " />
+                </group>
+                <group android:name="_R_G_L_0_G" android:translateX="14" android:translateY="13.5">
+                    <path android:name="_R_G_L_0_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="2.5"
+                          android:strokeAlpha="1"
+                          android:pathData=" M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " />
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueTo="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="2"
+                                android:valueTo="1.5"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.516,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueTo="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="2.5"
+                                android:valueTo="1.5"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.516,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueTo="M21.25 14.88 C21.25,14.88 21.25,10.74 21.25,10.74 C21.25,8.59 19.5,7.29 17.44,7.21 C15.24,7.13 13.5,8.47 13.5,10.62 C13.5,10.62 13.5,15.75 13.5,15.75 " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.347,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateX"
+                                android:duration="517"
+                                android:startOffset="0"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml
new file mode 100644
index 0000000..b6d76e0
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
+    <aapt:attr name="android:drawable">
+        <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_2_G_T_1" android:translateX="22.75" android:translateY="22.25" android:scaleX="1.02" android:scaleY="1.02">
+                    <group android:name="_R_G_L_2_G" android:translateX="-8.75" android:translateY="-8.75">
+                        <path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#FF000000"
+                              android:strokeLineJoin="round"
+                              android:strokeWidth="2.5"
+                              android:strokeAlpha="1"
+                              android:pathData=" M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " />
+                    </group>
+                </group>
+                <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+                    <path android:name="_R_G_L_1_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="2"
+                          android:strokeAlpha="1"
+                          android:pathData=" M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " />
+                </group>
+                <group android:name="_R_G_L_0_G" android:translateX="23" android:translateY="32.125">
+                    <path android:name="_R_G_L_0_G_D_0_P_0"
+                          android:fillColor="#FF000000"
+                          android:fillAlpha="1"
+                          android:fillType="nonZero"
+                          android:pathData=" M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " />
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333" android:startOffset="0" android:valueFrom="2.5" android:valueTo="2" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0.833,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="83" android:startOffset="0" android:valueFrom="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueTo="M27.13 10.19 C27.13,10.19 27.13,3.67 27.13,3.67 C27.13,0.3 24.38,-1.75 21.13,-1.87 C17.68,-2.01 14.94,0.11 14.94,3.49 C14.94,3.49 15,15 15,15 " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.456,0 0.464,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="133" android:startOffset="83" android:valueFrom="M27.13 10.19 C27.13,10.19 27.13,3.67 27.13,3.67 C27.13,0.3 24.38,-1.75 21.13,-1.87 C17.68,-2.01 14.94,0.11 14.94,3.49 C14.94,3.49 15,15 15,15 " android:valueTo="M2.5 10.38 C2.5,10.38 2.5,3.99 2.5,3.99 C2.5,0.61 5.3,-2.12 8.75,-2.12 C12.2,-2.12 15,0.61 15,3.99 C15,3.99 15,15 15,15 " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.606,0 0.035,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="117" android:startOffset="217" android:valueFrom="M2.5 10.38 C2.5,10.38 2.5,3.99 2.5,3.99 C2.5,0.61 5.3,-2.12 8.75,-2.12 C12.2,-2.12 15,0.61 15,3.99 C15,3.99 15,15 15,15 " android:valueTo="M2.5 15 C2.5,15 2.5,8.61 2.5,8.61 C2.5,5.24 5.3,2.5 8.75,2.5 C12.2,2.5 15,5.24 15,8.61 C15,8.61 15,15 15,15 " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.511,0 0.409,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateX"
+                                android:duration="333" android:startOffset="0" android:valueFrom="22.75" android:valueTo="23" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateY" android:duration="333" android:startOffset="0" android:valueFrom="22.25" android:valueTo="25.5" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_2_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="scaleX"
+                                android:duration="333" android:startOffset="0" android:valueFrom="1.02" android:valueTo="0.72" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleY"
+                                android:duration="333" android:startOffset="0" android:valueFrom="1.02" android:valueTo="0.72" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333" android:startOffset="0" android:valueFrom="2" android:valueTo="1.5" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.38,0 0.274,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333" android:startOffset="0" android:valueFrom="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueTo="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333" android:startOffset="0" android:valueFrom="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueTo="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateX"
+                                android:duration="850" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
index e9bd638..e80cfaf 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
@@ -25,6 +25,9 @@
     <!-- Margin around the various security views -->
     <dimen name="keyguard_security_view_top_margin">12dp</dimen>
 
+    <!-- Padding for the lock icon on the keyguard -->
+    <dimen name="lock_icon_padding">16dp</dimen>
+
     <!-- Overload default clock widget parameters -->
     <dimen name="widget_big_font_size">100dp</dimen>
     <dimen name="widget_label_font_size">18sp</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 4fc38a8..1601043 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -219,6 +219,9 @@
     <!-- Face hint message when finger was not recognized. [CHAR LIMIT=20] -->
     <string name="kg_face_not_recognized">Not recognized</string>
 
+     <!-- Error message indicating that the camera privacy sensor has been turned on [CHAR LIMIT=NONE] -->
+    <string name="kg_face_sensor_privacy_enabled">To use Face Unlock, turn on <b>Camera access</b> in Settings > Privacy</string>
+
     <!-- Instructions telling the user remaining times when enter SIM PIN view.  -->
     <plurals name="kg_password_default_pin_message">
         <item quantity="one">Enter SIM PIN. You have <xliff:g id="number">%d</xliff:g> remaining
diff --git a/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml b/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml
deleted file mode 100644
index 50267fd..0000000
--- a/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-       android:shape="rectangle">
-    <stroke
-        android:color="?androidprv:attr/colorAccentPrimaryVariant"
-        android:width="1dp"/>
-    <corners android:radius="20dp"/>
-    <padding
-        android:left="8dp"
-        android:right="8dp"
-        android:top="4dp"
-        android:bottom="4dp" />
-    <solid android:color="@android:color/transparent" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
index 665b456..a47299d 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
@@ -29,7 +29,7 @@
             <shape android:shape="rectangle">
                 <corners android:radius="?android:attr/buttonCornerRadius"/>
                 <solid android:color="@android:color/transparent"/>
-                <stroke android:color="?androidprv:attr/colorAccentPrimary"
+                <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant"
                         android:width="1dp"
                 />
                 <padding android:left="@dimen/dialog_button_horizontal_padding"
diff --git a/packages/SystemUI/res/drawable/screenrecord_button_background_outline.xml b/packages/SystemUI/res/drawable/screenrecord_button_background_outline.xml
deleted file mode 100644
index 59a31e8..0000000
--- a/packages/SystemUI/res/drawable/screenrecord_button_background_outline.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-       android:shape="rectangle">
-    <stroke
-        android:color="?androidprv:attr/colorAccentPrimary"
-        android:width="1dp"/>
-    <corners android:radius="24dp"/>
-    <padding
-        android:left="16dp"
-        android:right="16dp"
-        android:top="8dp"
-        android:bottom="8dp" />
-    <solid android:color="@android:color/transparent" />
-</shape>
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index 3c9e44e..89690e8 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -73,6 +73,9 @@
         android:accessibilityLiveRegion="polite"
         android:singleLine="true"
         android:ellipsize="marquee"
+        android:marqueeRepeatLimit="marquee_forever"
+        android:scrollHorizontally="true"
+        android:fadingEdge="horizontal"
         android:textColor="@color/biometric_dialog_gray"/>
 
     <LinearLayout
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index e90a644..275e0a5 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -37,7 +37,7 @@
             android:ellipsize="end"
             android:gravity="center_vertical|center_horizontal"
             android:layout_width="wrap_content"
-            android:layout_height="32dp"
+            android:layout_height="wrap_content"
             android:textAppearance="@style/TextAppearance.InternetDialog"
             android:textSize="24sp"/>
 
@@ -45,7 +45,7 @@
             android:id="@+id/internet_dialog_subtitle"
             android:gravity="center_vertical|center_horizontal"
             android:layout_width="wrap_content"
-            android:layout_height="20dp"
+            android:layout_height="wrap_content"
             android:layout_marginTop="4dp"
             android:ellipsize="end"
             android:maxLines="1"
@@ -67,7 +67,6 @@
 
         <ProgressBar
             android:id="@+id/wifi_searching_progress"
-            android:indeterminate="true"
             android:layout_width="340dp"
             android:layout_height="wrap_content"
             android:layout_gravity="center_horizontal"
@@ -151,6 +150,7 @@
                         android:gravity="start|center_vertical">
                         <TextView
                             android:id="@+id/mobile_title"
+                            android:maxLines="1"
                             style="@style/InternetDialog.NetworkTitle"/>
                         <TextView
                             android:id="@+id/mobile_summary"
@@ -381,54 +381,44 @@
                 android:id="@+id/button_layout"
                 android:orientation="horizontal"
                 android:layout_width="match_parent"
-                android:layout_height="48dp"
-                android:layout_marginStart="24dp"
-                android:layout_marginEnd="24dp"
+                android:layout_height="wrap_content"
                 android:layout_marginTop="8dp"
-                android:layout_marginBottom="34dp"
+                android:layout_marginStart="@dimen/dialog_side_padding"
+                android:layout_marginEnd="@dimen/dialog_side_padding"
+                android:layout_marginBottom="@dimen/dialog_bottom_padding"
                 android:clickable="false"
                 android:focusable="false">
 
                 <FrameLayout
                     android:id="@+id/apm_layout"
                     android:layout_width="wrap_content"
-                    android:layout_height="48dp"
+                    android:layout_height="wrap_content"
                     android:clickable="true"
                     android:focusable="true"
                     android:layout_gravity="start|center_vertical"
                     android:orientation="vertical">
                     <Button
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
                         android:text="@string/turn_off_airplane_mode"
                         android:ellipsize="end"
-                        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-                        android:layout_width="wrap_content"
-                        android:layout_height="36dp"
-                        android:layout_gravity="start|center_vertical"
-                        android:textAppearance="@style/TextAppearance.InternetDialog"
-                        android:textSize="14sp"
-                        android:background="@drawable/internet_dialog_footer_background"
+                        style="@style/Widget.Dialog.Button.BorderButton"
                         android:clickable="false"/>
                 </FrameLayout>
 
                 <FrameLayout
                     android:id="@+id/done_layout"
                     android:layout_width="wrap_content"
-                    android:layout_height="48dp"
+                    android:layout_height="wrap_content"
                     android:layout_marginStart="16dp"
-                    android:clickable="true"
-                    android:focusable="true"
                     android:layout_gravity="end|center_vertical"
-                    android:orientation="vertical">
+                    android:clickable="true"
+                    android:focusable="true">
                     <Button
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
                         android:text="@string/inline_done_button"
-                        android:ellipsize="end"
-                        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-                        android:layout_width="67dp"
-                        android:layout_height="36dp"
-                        android:layout_gravity="end|center_vertical"
-                        android:textAppearance="@style/TextAppearance.InternetDialog"
-                        android:textSize="14sp"
-                        android:background="@drawable/internet_dialog_footer_background"
+                        style="@style/Widget.Dialog.Button"
                         android:clickable="false"/>
                 </FrameLayout>
             </FrameLayout>
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index 07fd1b0..3a186d2 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -89,17 +89,16 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="16dp"
-        android:layout_marginStart="24dp"
-        android:layout_marginBottom="24dp"
-        android:layout_marginEnd="24dp"
+        android:layout_marginStart="@dimen/dialog_side_padding"
+        android:layout_marginEnd="@dimen/dialog_side_padding"
+        android:layout_marginBottom="@dimen/dialog_bottom_padding"
         android:orientation="horizontal">
 
         <Button
             android:id="@+id/stop"
-            style="@style/MediaOutputRoundedOutlinedButton"
+            style="@style/Widget.Dialog.Button.BorderButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:minWidth="0dp"
             android:text="@string/keyboard_key_media_stop"
             android:visibility="gone"/>
 
@@ -110,10 +109,9 @@
 
         <Button
             android:id="@+id/done"
-            style="@style/MediaOutputRoundedOutlinedButton"
+            style="@style/Widget.Dialog.Button"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:minWidth="0dp"
             android:text="@string/inline_done_button"/>
     </LinearLayout>
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index 6c5ad50..6012b58 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -27,10 +27,10 @@
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:paddingStart="24dp"
-            android:paddingEnd="24dp"
-            android:paddingTop="26dp"
-            android:paddingBottom="30dp"
+            android:paddingStart="@dimen/dialog_side_padding"
+            android:paddingEnd="@dimen/dialog_side_padding"
+            android:paddingTop="@dimen/dialog_top_padding"
+            android:paddingBottom="@dimen/dialog_bottom_padding"
             android:orientation="vertical">
 
             <!-- Header -->
@@ -108,10 +108,7 @@
                     android:layout_weight="0"
                     android:layout_gravity="start"
                     android:text="@string/cancel"
-                    android:textColor="?android:textColorPrimary"
-                    android:background="@drawable/screenrecord_button_background_outline"
-                    android:textAppearance="?android:attr/textAppearanceMedium"
-                    android:textSize="14sp"/>
+                    style="@style/Widget.Dialog.Button.BorderButton" />
                 <Space
                     android:layout_width="0dp"
                     android:layout_height="match_parent"
@@ -123,10 +120,7 @@
                     android:layout_weight="0"
                     android:layout_gravity="end"
                     android:text="@string/screenrecord_start"
-                    android:textColor="@android:color/system_neutral1_900"
-                    android:background="@drawable/screenrecord_button_background_solid"
-                    android:textAppearance="?android:attr/textAppearanceMedium"
-                    android:textSize="14sp"/>
+                    style="@style/Widget.Dialog.Button" />
             </LinearLayout>
         </LinearLayout>
     </ScrollView>
diff --git a/packages/SystemUI/res/layout/status_bar_no_notifications.xml b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
index 332dc6ee..a2abdb2 100644
--- a/packages/SystemUI/res/layout/status_bar_no_notifications.xml
+++ b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
@@ -26,8 +26,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:minHeight="64dp"
-            android:paddingTop="12dp"
             android:textAppearance="?android:attr/textAppearanceButton"
-            android:gravity="top|center_horizontal"
+            android:gravity="center"
             android:text="@string/empty_shade_text"/>
 </com.android.systemui.statusbar.EmptyShadeView>
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml
index f057603..1d6f279 100644
--- a/packages/SystemUI/res/values-h800dp/dimens.xml
+++ b/packages/SystemUI/res/values-h800dp/dimens.xml
@@ -22,5 +22,5 @@
     <dimen name="large_clock_text_size">200dp</dimen>
 
     <!-- With the large clock, move up slightly from the center -->
-    <dimen name="keyguard_large_clock_top_margin">-104dp</dimen>
+    <dimen name="keyguard_large_clock_top_margin">-112dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index b98694e..fcb3698 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -77,7 +77,7 @@
     <color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 -->
 
     <!-- UDFPS colors -->
-    <color name="udfps_enroll_icon">#ffffff</color> <!-- 100% white -->
+    <color name="udfps_enroll_icon">#7DA7F1</color>
 
     <color name="GM2_green_500">#FF41Af6A</color>
     <color name="GM2_blue_500">#5195EA</color>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8a3baca..e455aaa 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -134,10 +134,10 @@
     <color name="biometric_dialog_error">#ffd93025</color>                  <!-- red 600 -->
 
     <!-- UDFPS colors -->
-    <color name="udfps_enroll_icon">#000000</color>                         <!-- 100% black -->
-    <color name="udfps_moving_target_fill">#cc4285f4</color>                <!-- 80% blue -->
-    <color name="udfps_enroll_progress">#ff669DF6</color>                   <!-- blue 400 -->
-    <color name="udfps_enroll_progress_help">#ffEE675C</color>              <!-- red 400 -->
+    <color name="udfps_enroll_icon">#7DA7F1</color>
+    <color name="udfps_moving_target_fill">#475670</color>
+    <color name="udfps_enroll_progress">#7DA7F1</color>
+    <color name="udfps_enroll_progress_help">#ffEE675C</color>
 
     <!-- Global screenshot actions -->
     <color name="global_screenshot_button_ripple">#1f000000</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 475f70f..e00b941 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -595,17 +595,6 @@
         280
     </integer>
 
-    <!-- Haptic feedback intensity for ticks used for the udfps dwell time -->
-    <item name="config_udfpsTickIntensity" translatable="false" format="float"
-          type="dimen">.5</item>
-
-    <!-- Haptic feedback delay between ticks used for udfps dwell time -->
-    <integer name="config_udfpsTickDelay" translatable="false">25</integer>
-
-    <!-- Haptic feedback tick type - if true, uses VibrationEffect.Composition.PRIMITIVE_LOW_TICK
-         else uses VibrationEffect.Composition.PRIMITIVE_TICK -->
-    <bool name="config_udfpsUseLowTick">true</bool>
-
     <!-- package name of a built-in camera app to use to restrict implicit intent resolution
          when the double-press power gesture is used. Ignored if empty. -->
     <string translatable="false" name="config_cameraGesturePackage"></string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1938e48..a67d5c2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -601,7 +601,7 @@
     <!-- When large clock is showing, offset the smartspace by this amount -->
     <dimen name="keyguard_smartspace_top_offset">12dp</dimen>
     <!-- With the large clock, move up slightly from the center -->
-    <dimen name="keyguard_large_clock_top_margin">-52dp</dimen>
+    <dimen name="keyguard_large_clock_top_margin">-60dp</dimen>
 
     <!-- Default line spacing multiplier between hours and minutes of the keyguard clock -->
     <item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4dc52e7..e1afd3f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -630,7 +630,7 @@
     <!-- QuickSettings: Brightness dialog title [CHAR LIMIT=NONE] -->
     <string name="quick_settings_brightness_dialog_title">Brightness</string>
     <!-- QuickSettings: Label for the toggle that controls whether display inversion is enabled. [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_inversion_label">Invert colors</string>
+    <string name="quick_settings_inversion_label">Color inversion</string>
     <!-- QuickSettings: Label for the toggle that controls whether display color correction is enabled. [CHAR LIMIT=NONE] -->
     <!-- QuickSettings: Control panel: Label for button that navigates to settings. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_more_settings">More settings</string>
@@ -2037,8 +2037,8 @@
     <string name="magnification_mode_switch_click_label">Switch</string>
 
     <!-- Accessibility floating menu strings -->
-    <!-- Message for the accessibility floating button migration tooltip. It shows when the user use gestural navigation then upgrade their system. It will tell the user the accessibility gesture had been replaced by accessibility floating button. [CHAR LIMIT=100] -->
-    <string name="accessibility_floating_button_migration_tooltip">Accessibility button replaced the accessibility gesture\n\n<annotation id="link">View settings</annotation></string>
+    <!-- Message for the accessibility floating button migration tooltip. It shows when the user use gestural navigation then upgrade their system. It will tell the user they could customize or replace the floating button in Settings. [CHAR LIMIT=100] -->
+    <string name="accessibility_floating_button_migration_tooltip">Tap to open accessibility features. Customize or replace this button in Settings.\n\n<annotation id="link">View settings</annotation></string>
     <!-- Message for the accessibility floating button docking tooltip. It shows when the user first time drag the button. It will tell the user about docking behavior. [CHAR LIMIT=70] -->
     <string name="accessibility_floating_button_docking_tooltip">Move button to the edge to hide it temporarily</string>
     <!-- Action in accessibility menu to move the accessibility floating button to the top left of the screen. [CHAR LIMIT=30] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 92985de..051a30c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -450,10 +450,6 @@
         <item name="android:background">@drawable/btn_borderless_rect</item>
     </style>
 
-    <style name="MediaOutputRoundedOutlinedButton" parent="@android:style/Widget.Material.Button">
-        <item name="android:background">@drawable/media_output_dialog_button_background</item>
-    </style>
-
     <style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings">
         <item name="android:windowActionBar">false</item>
         <item name="preferenceTheme">@style/TunerPreferenceTheme</item>
@@ -881,13 +877,18 @@
         <item name="android:textAlignment">center</item>
     </style>
 
-    <style name="Widget.Dialog.Button" parent = "Theme.SystemUI.Dialog">
+
+    <style name="Widget" />
+    <style name="Widget.Dialog" />
+    <style name="Widget.Dialog.Button">
+        <item name="android:buttonCornerRadius">28dp</item>
         <item name="android:background">@drawable/qs_dialog_btn_filled</item>
         <item name="android:textColor">?androidprv:attr/textColorOnAccent</item>
         <item name="android:textSize">14sp</item>
         <item name="android:lineHeight">20sp</item>
         <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
         <item name="android:stateListAnimator">@null</item>
+        <item name="android:minWidth">0dp</item>
     </style>
 
     <style name="Widget.Dialog.Button.BorderButton">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 7d0fb5d..567e7aa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -53,8 +53,8 @@
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
                 .setPosition(leash, positionX, positionY)
                 .setCornerRadius(leash, cornerRadius);
-        return new PictureInPictureSurfaceTransaction(
-                positionX, positionY, mTmpFloat9, 0 /* rotation */, cornerRadius, sourceBounds);
+        return newPipSurfaceTransaction(positionX, positionY,
+                mTmpFloat9, 0 /* rotation */, cornerRadius, sourceBounds);
     }
 
     public PictureInPictureSurfaceTransaction scale(
@@ -70,8 +70,8 @@
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
                 .setPosition(leash, positionX, positionY)
                 .setCornerRadius(leash, cornerRadius);
-        return new PictureInPictureSurfaceTransaction(
-                positionX, positionY, mTmpFloat9, degree, cornerRadius, sourceBounds);
+        return newPipSurfaceTransaction(positionX, positionY,
+                mTmpFloat9, degree, cornerRadius, sourceBounds);
     }
 
     public PictureInPictureSurfaceTransaction scaleAndCrop(
@@ -93,8 +93,8 @@
                 .setWindowCrop(leash, mTmpDestinationRect)
                 .setPosition(leash, left, top)
                 .setCornerRadius(leash, cornerRadius);
-        return new PictureInPictureSurfaceTransaction(
-                left, top, mTmpFloat9, 0 /* rotation */, cornerRadius, mTmpDestinationRect);
+        return newPipSurfaceTransaction(left, top,
+                mTmpFloat9, 0 /* rotation */, cornerRadius, mTmpDestinationRect);
     }
 
     public PictureInPictureSurfaceTransaction scaleAndRotate(
@@ -125,8 +125,7 @@
                 .setWindowCrop(leash, mTmpDestinationRect)
                 .setPosition(leash, adjustedPositionX, adjustedPositionY)
                 .setCornerRadius(leash, cornerRadius);
-        return new PictureInPictureSurfaceTransaction(
-                adjustedPositionX, adjustedPositionY,
+        return newPipSurfaceTransaction(adjustedPositionX, adjustedPositionY,
                 mTmpFloat9, degree, cornerRadius, mTmpDestinationRect);
     }
 
@@ -137,6 +136,17 @@
         return mCornerRadius * scale;
     }
 
+    private static PictureInPictureSurfaceTransaction newPipSurfaceTransaction(
+            float posX, float posY, float[] float9, float rotation, float cornerRadius,
+            Rect windowCrop) {
+        return new PictureInPictureSurfaceTransaction.Builder()
+                .setPosition(posX, posY)
+                .setTransform(float9, rotation)
+                .setCornerRadius(cornerRadius)
+                .setWindowCrop(windowCrop)
+                .build();
+    }
+
     /** @return {@link SurfaceControl.Transaction} instance with vsync-id */
     public static SurfaceControl.Transaction newSurfaceControlTransaction() {
         final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 78867f7..605d376 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -36,6 +36,7 @@
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.util.Log;
+import android.view.HapticFeedbackConstants;
 import android.view.IRotationWatcher;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -453,6 +454,7 @@
         mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
         incrementNumAcceptedRotationSuggestionsIfNeeded();
         setRotationLockedAtAngle(mLastRotationSuggestion);
+        v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
     }
 
     private boolean onRotateSuggestionHover(View v, MotionEvent event) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index e46b6f1..ea93a3b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -60,14 +60,12 @@
         hingeAngleProvider,
         screenStatusProvider,
         deviceStateManager,
-        mainExecutor
+        mainExecutor,
+        mainHandler
     )
 
     val unfoldTransitionProgressProvider = if (config.isHingeAngleEnabled) {
-        PhysicsBasedUnfoldTransitionProgressProvider(
-            mainHandler,
-            foldStateProvider
-        )
+        PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
     } else {
         FixedTimingTransitionProgressProvider(foldStateProvider)
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index 90f5998..51eae57 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -15,7 +15,6 @@
  */
 package com.android.systemui.unfold.progress
 
-import android.os.Handler
 import android.util.Log
 import android.util.MathUtils.saturate
 import androidx.dynamicanimation.animation.DynamicAnimation
@@ -24,9 +23,10 @@
 import androidx.dynamicanimation.animation.SpringForce
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.updates.FOLD_UPDATE_ABORTED
 import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
-import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
 import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
 import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
 import com.android.systemui.unfold.updates.FoldStateProvider
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
@@ -39,7 +39,6 @@
  *  - doesn't handle postures
  */
 internal class PhysicsBasedUnfoldTransitionProgressProvider(
-    private val handler: Handler,
     private val foldStateProvider: FoldStateProvider
 ) :
     UnfoldTransitionProgressProvider,
@@ -51,8 +50,6 @@
             addEndListener(this@PhysicsBasedUnfoldTransitionProgressProvider)
         }
 
-    private val timeoutRunnable = TimeoutRunnable()
-
     private var isTransitionRunning = false
     private var isAnimatedCancelRunning = false
 
@@ -92,7 +89,7 @@
                     cancelTransition(endValue = 1f, animate = true)
                 }
             }
-            FOLD_UPDATE_FINISH_FULL_OPEN -> {
+            FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_ABORTED -> {
                 // Do not cancel if we haven't started the transition yet.
                 // This could happen when we fully unfolded the device before the screen
                 // became available. In this case we start and immediately cancel the animation
@@ -106,7 +103,11 @@
                 cancelTransition(endValue = 0f, animate = false)
             }
             FOLD_UPDATE_START_CLOSING -> {
-                startTransition(startValue = 1f)
+                // The transition might be already running as the device might start closing several
+                // times before reaching an end state.
+                if (!isTransitionRunning) {
+                    startTransition(startValue = 1f)
+                }
             }
         }
 
@@ -116,8 +117,6 @@
     }
 
     private fun cancelTransition(endValue: Float, animate: Boolean) {
-        handler.removeCallbacks(timeoutRunnable)
-
         if (isTransitionRunning && animate) {
             isAnimatedCancelRunning = true
             springAnimation.animateToFinalPosition(endValue)
@@ -175,8 +174,6 @@
         }
 
         springAnimation.start()
-
-        handler.postDelayed(timeoutRunnable, TRANSITION_TIMEOUT_MILLIS)
     }
 
     override fun addCallback(listener: TransitionProgressListener) {
@@ -187,13 +184,6 @@
         listeners.remove(listener)
     }
 
-    private inner class TimeoutRunnable : Runnable {
-
-        override fun run() {
-            cancelTransition(endValue = 1f, animate = true)
-        }
-    }
-
     private object AnimationProgressProperty :
         FloatPropertyCompat<PhysicsBasedUnfoldTransitionProgressProvider>("animation_progress") {
 
@@ -212,7 +202,6 @@
 private const val TAG = "PhysicsBasedUnfoldTransitionProgressProvider"
 private const val DEBUG = true
 
-private const val TRANSITION_TIMEOUT_MILLIS = 2000L
 private const val SPRING_STIFFNESS = 200.0f
 private const val MINIMAL_VISIBLE_CHANGE = 0.001f
 private const val FINAL_HINGE_ANGLE_POSITION = 165f
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 35e2b30..6d9631c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -15,14 +15,19 @@
  */
 package com.android.systemui.unfold.updates
 
+import android.annotation.FloatRange
 import android.content.Context
 import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import android.util.Log
+import androidx.annotation.VisibleForTesting
 import androidx.core.util.Consumer
-import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
+import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES
 import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES
 import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
 import java.util.concurrent.Executor
 
 class DeviceFoldStateProvider(
@@ -30,7 +35,8 @@
     private val hingeAngleProvider: HingeAngleProvider,
     private val screenStatusProvider: ScreenStatusProvider,
     private val deviceStateManager: DeviceStateManager,
-    private val mainExecutor: Executor
+    private val mainExecutor: Executor,
+    private val handler: Handler
 ) : FoldStateProvider {
 
     private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
@@ -38,9 +44,13 @@
     @FoldUpdate
     private var lastFoldUpdate: Int? = null
 
+    @FloatRange(from = 0.0, to = 180.0)
+    private var lastHingeAngle: Float = 0f
+
     private val hingeAngleListener = HingeAngleListener()
     private val screenListener = ScreenStatusListener()
     private val foldStateListener = FoldStateListener(context)
+    private val timeoutRunnable = TimeoutRunnable()
 
     private var isFolded = false
     private var isUnfoldHandled = true
@@ -72,47 +82,69 @@
     override val isFullyOpened: Boolean
         get() = !isFolded && lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN
 
+    private val isTransitionInProgess: Boolean
+        get() = lastFoldUpdate == FOLD_UPDATE_START_OPENING ||
+                lastFoldUpdate == FOLD_UPDATE_START_CLOSING
+
     private fun onHingeAngle(angle: Float) {
-        when (lastFoldUpdate) {
-            FOLD_UPDATE_FINISH_FULL_OPEN -> {
-                if (FULLY_OPEN_DEGREES - angle > START_CLOSING_THRESHOLD_DEGREES) {
-                    lastFoldUpdate = FOLD_UPDATE_START_CLOSING
-                    outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_CLOSING) }
-                }
-            }
-            FOLD_UPDATE_START_OPENING -> {
-                if (FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES) {
-                    lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
-                    outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
-                }
-            }
-            FOLD_UPDATE_START_CLOSING -> {
-                if (FULLY_OPEN_DEGREES - angle < START_CLOSING_THRESHOLD_DEGREES) {
-                    lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
-                    outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
-                }
+        if (DEBUG) { Log.d(TAG, "Hinge angle: $angle, lastHingeAngle: $lastHingeAngle") }
+
+        val isClosing = angle < lastHingeAngle
+        val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES
+        val closingEventDispatched = lastFoldUpdate == FOLD_UPDATE_START_CLOSING
+
+        if (isClosing && !closingEventDispatched && !isFullyOpened) {
+            notifyFoldUpdate(FOLD_UPDATE_START_CLOSING)
+        }
+
+        if (isTransitionInProgess) {
+            if (isFullyOpened) {
+                notifyFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+                cancelTimeout()
+            } else {
+                // The timeout will trigger some constant time after the last angle update.
+                rescheduleAbortAnimationTimeout()
             }
         }
 
+        lastHingeAngle = angle
         outputListeners.forEach { it.onHingeAngleUpdate(angle) }
     }
 
     private inner class FoldStateListener(context: Context) :
         DeviceStateManager.FoldStateListener(context, { folded: Boolean ->
             isFolded = folded
+            lastHingeAngle = FULLY_CLOSED_DEGREES
 
             if (folded) {
-                lastFoldUpdate = FOLD_UPDATE_FINISH_CLOSED
-                outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) }
                 hingeAngleProvider.stop()
+                notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+                cancelTimeout()
                 isUnfoldHandled = false
             } else {
-                lastFoldUpdate = FOLD_UPDATE_START_OPENING
-                outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_OPENING) }
+                notifyFoldUpdate(FOLD_UPDATE_START_OPENING)
+                rescheduleAbortAnimationTimeout()
                 hingeAngleProvider.start()
             }
         })
 
+    private fun notifyFoldUpdate(@FoldUpdate update: Int) {
+        if (DEBUG) { Log.d(TAG, stateToString(update)) }
+        outputListeners.forEach { it.onFoldUpdate(update) }
+        lastFoldUpdate = update
+    }
+
+    private fun rescheduleAbortAnimationTimeout() {
+        if (isTransitionInProgess) {
+            cancelTimeout()
+        }
+        handler.postDelayed(timeoutRunnable, ABORT_CLOSING_MILLIS)
+    }
+
+    private fun cancelTimeout() {
+        handler.removeCallbacks(timeoutRunnable)
+    }
+
     private inner class ScreenStatusListener :
         ScreenStatusProvider.ScreenListener {
 
@@ -136,7 +168,39 @@
             onHingeAngle(angle)
         }
     }
+
+    private inner class TimeoutRunnable : Runnable {
+
+        override fun run() {
+            notifyFoldUpdate(FOLD_UPDATE_ABORTED)
+        }
+    }
 }
 
-private const val START_CLOSING_THRESHOLD_DEGREES = 95f
-private const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
\ No newline at end of file
+private fun stateToString(@FoldUpdate update: Int): String {
+    return when (update) {
+        FOLD_UPDATE_START_OPENING -> "START_OPENING"
+        FOLD_UPDATE_HALF_OPEN -> "HALF_OPEN"
+        FOLD_UPDATE_START_CLOSING -> "START_CLOSING"
+        FOLD_UPDATE_ABORTED -> "ABORTED"
+        FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> "UNFOLDED_SCREEN_AVAILABLE"
+        FOLD_UPDATE_FINISH_HALF_OPEN -> "FINISH_HALF_OPEN"
+        FOLD_UPDATE_FINISH_FULL_OPEN -> "FINISH_FULL_OPEN"
+        FOLD_UPDATE_FINISH_CLOSED -> "FINISH_CLOSED"
+        else -> "UNKNOWN"
+    }
+}
+
+private const val TAG = "DeviceFoldProvider"
+private const val DEBUG = false
+
+/**
+ * Time after which [FOLD_UPDATE_ABORTED] is emitted following a [FOLD_UPDATE_START_CLOSING] or
+ * [FOLD_UPDATE_START_OPENING] event, if an end state is not reached.
+ */
+@VisibleForTesting
+const val ABORT_CLOSING_MILLIS = 1000L
+
+/** Threshold after which we consider the device fully unfolded. */
+@VisibleForTesting
+const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index 643ece3..bffebcd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -39,6 +39,7 @@
         FOLD_UPDATE_START_OPENING,
         FOLD_UPDATE_HALF_OPEN,
         FOLD_UPDATE_START_CLOSING,
+        FOLD_UPDATE_ABORTED,
         FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE,
         FOLD_UPDATE_FINISH_HALF_OPEN,
         FOLD_UPDATE_FINISH_FULL_OPEN,
@@ -51,7 +52,8 @@
 const val FOLD_UPDATE_START_OPENING = 0
 const val FOLD_UPDATE_HALF_OPEN = 1
 const val FOLD_UPDATE_START_CLOSING = 2
-const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 3
-const val FOLD_UPDATE_FINISH_HALF_OPEN = 4
-const val FOLD_UPDATE_FINISH_FULL_OPEN = 5
-const val FOLD_UPDATE_FINISH_CLOSED = 6
+const val FOLD_UPDATE_ABORTED = 3
+const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 4
+const val FOLD_UPDATE_FINISH_HALF_OPEN = 5
+const val FOLD_UPDATE_FINISH_FULL_OPEN = 6
+const val FOLD_UPDATE_FINISH_CLOSED = 7
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index c628d44..324fae1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -89,7 +89,6 @@
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final KeyguardBypassController mBypassController;
 
-    private int mLargeClockTopMargin = 0;
     private int mKeyguardClockTopMargin = 0;
 
     /**
@@ -276,16 +275,13 @@
     }
 
     private void updateClockLayout() {
-        if (mSmartspaceController.isEnabled()) {
-            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
-                    MATCH_PARENT);
-            mLargeClockTopMargin = getContext().getResources().getDimensionPixelSize(
-                    R.dimen.keyguard_large_clock_top_margin);
-            lp.topMargin = mLargeClockTopMargin;
-            mLargeClockFrame.setLayoutParams(lp);
-        } else {
-            mLargeClockTopMargin = 0;
-        }
+        int largeClockTopMargin = getContext().getResources().getDimensionPixelSize(
+                R.dimen.keyguard_large_clock_top_margin);
+
+        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
+                MATCH_PARENT);
+        lp.topMargin = largeClockTopMargin;
+        mLargeClockFrame.setLayoutParams(lp);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index ba67716..2789e27 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -51,6 +51,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
+import android.hardware.SensorPrivacyManager;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricSourceType;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -335,6 +336,8 @@
     private boolean mLockIconPressed;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private final Executor mBackgroundExecutor;
+    private SensorPrivacyManager mSensorPrivacyManager;
+    private int mFaceAuthUserId;
 
     /**
      * Short delay before restarting fingerprint authentication after a successful try. This should
@@ -827,7 +830,9 @@
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
             lockedOutStateChanged |= !mFingerprintLockedOutPermanent;
             mFingerprintLockedOutPermanent = true;
-            requireStrongAuthIfAllLockedOut();
+            Log.d(TAG, "Fingerprint locked out - requiring strong auth");
+            mLockPatternUtils.requireStrongAuth(
+                    STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, getCurrentUser());
         }
 
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
@@ -837,6 +842,7 @@
             if (isUdfpsEnrolled()) {
                 updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
             }
+            stopListeningForFace();
         }
 
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1016,6 +1022,12 @@
 
         // Error is always the end of authentication lifecycle
         mFaceCancelSignal = null;
+        boolean cameraPrivacyEnabled = false;
+        if (mSensorPrivacyManager != null) {
+            cameraPrivacyEnabled = mSensorPrivacyManager
+                    .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA,
+                    mFaceAuthUserId);
+        }
 
         if (msgId == FaceManager.FACE_ERROR_CANCELED
                 && mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
@@ -1025,7 +1037,9 @@
             setFaceRunningState(BIOMETRIC_STATE_STOPPED);
         }
 
-        if (msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE
+        final boolean isHwUnavailable = msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE;
+
+        if (isHwUnavailable
                 || msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS) {
             if (mHardwareFaceUnavailableRetryCount < HAL_ERROR_RETRY_MAX) {
                 mHardwareFaceUnavailableRetryCount++;
@@ -1038,7 +1052,10 @@
         if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
             lockedOutStateChanged = !mFaceLockedOutPermanent;
             mFaceLockedOutPermanent = true;
-            requireStrongAuthIfAllLockedOut();
+        }
+
+        if (isHwUnavailable && cameraPrivacyEnabled) {
+            errString = mContext.getString(R.string.kg_face_sensor_privacy_enabled);
         }
 
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1148,19 +1165,6 @@
         return faceAuthenticated;
     }
 
-    private void requireStrongAuthIfAllLockedOut() {
-        final boolean faceLock =
-                (mFaceLockedOutPermanent || !shouldListenForFace()) && !getIsFaceAuthenticated();
-        final boolean fpLock =
-                mFingerprintLockedOutPermanent || !shouldListenForFingerprint(isUdfpsEnrolled());
-
-        if (faceLock && fpLock) {
-            Log.d(TAG, "All biometrics locked out - requiring strong auth");
-            mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
-                    getCurrentUser());
-        }
-    }
-
     public boolean getUserCanSkipBouncer(int userId) {
         return getUserHasTrust(userId) || getUserUnlockedWithBiometric(userId);
     }
@@ -1816,6 +1820,7 @@
         mLockPatternUtils = lockPatternUtils;
         mAuthController = authController;
         dumpManager.registerDumpable(getClass().getName(), this);
+        mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
 
         mHandler = new Handler(mainLooper) {
             @Override
@@ -2357,6 +2362,9 @@
                 containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
                         || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
 
+        // TODO: always disallow when fp is already locked out?
+        final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+
         final boolean canBypass = mKeyguardBypassController != null
                 && mKeyguardBypassController.canBypass();
         // There's no reason to ask the HAL for authentication when the user can dismiss the
@@ -2391,7 +2399,8 @@
                 && !mKeyguardGoingAway && biometricEnabledForUser && !mLockIconPressed
                 && strongAuthAllowsScanning && mIsPrimaryUser
                 && (!mSecureCameraLaunched || mOccludingAppRequestingFace)
-                && !faceAuthenticated;
+                && !faceAuthenticated
+                && !fpLockedout;
 
         // Aggregate relevant fields for debug logging.
         if (DEBUG_FACE || DEBUG_SPEW) {
@@ -2517,6 +2526,7 @@
             // This would need to be updated for multi-sensor devices
             final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty()
                     && mFaceSensorProperties.get(0).supportsFaceDetection;
+            mFaceAuthUserId = userId;
             if (isEncryptedOrLockdown(userId) && supportsFaceDetection) {
                 mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, userId);
             } else {
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index e267c5c..cc452c6 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -35,12 +35,10 @@
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.media.AudioAttributes;
 import android.os.Process;
-import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.MathUtils;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -68,8 +66,6 @@
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
-import com.airbnb.lottie.LottieAnimationView;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Objects;
@@ -104,10 +100,8 @@
     @NonNull private final AccessibilityManager mAccessibilityManager;
     @NonNull private final ConfigurationController mConfigurationController;
     @NonNull private final DelayableExecutor mExecutor;
-    @NonNull private final LayoutInflater mLayoutInflater;
     private boolean mUdfpsEnrolled;
 
-    @Nullable private LottieAnimationView mAodFp;
     @NonNull private final AnimatedStateListDrawable mIcon;
 
     @NonNull private CharSequence mUnlockedLabel;
@@ -119,7 +113,6 @@
     private VelocityTracker mVelocityTracker;
     // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active.
     private int mActivePointerId = -1;
-    private VibrationEffect mTick;
 
     private boolean mIsDozing;
     private boolean mIsBouncerShowing;
@@ -143,7 +136,7 @@
 
     // for udfps when strong auth is required or unlocked on AOD
     private boolean mShowAodLockIcon;
-    private boolean mShowAODFpIcon;
+    private boolean mShowAodUnlockedIcon;
     private final int mMaxBurnInOffsetX;
     private final int mMaxBurnInOffsetY;
     private float mInterpolatedDarkAmount;
@@ -166,8 +159,7 @@
             @NonNull @Main DelayableExecutor executor,
             @Nullable Vibrator vibrator,
             @Nullable AuthRippleController authRippleController,
-            @NonNull @Main Resources resources,
-            @NonNull LayoutInflater inflater
+            @NonNull @Main Resources resources
     ) {
         super(view);
         mStatusBarStateController = statusBarStateController;
@@ -181,7 +173,6 @@
         mExecutor = executor;
         mVibrator = vibrator;
         mAuthRippleController = authRippleController;
-        mLayoutInflater = inflater;
 
         mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
         mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
@@ -263,11 +254,12 @@
         }
 
         boolean wasShowingUnlock = mShowUnlockIcon;
-        boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon;
+        boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon
+                && !mShowAodUnlockedIcon && !mShowAodLockIcon;
         mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen()
                 && (!mUdfpsEnrolled || !mRunningFPS);
         mShowUnlockIcon = (mCanDismissLockScreen || mUserUnlockedWithBiometric) && isLockScreen();
-        mShowAODFpIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
+        mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
         mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen;
 
         final CharSequence prevContentDescription = mView.getContentDescription();
@@ -284,20 +276,11 @@
             mView.updateIcon(ICON_UNLOCK, false);
             mView.setContentDescription(mUnlockedLabel);
             mView.setVisibility(View.VISIBLE);
-        } else if (mShowAODFpIcon) {
-            // AOD fp icon is special cased as a lottie view (it updates for each burn-in offset),
-            // this state shows a transparent view
-            mView.setContentDescription(null);
-            mAodFp.setVisibility(View.VISIBLE);
-            mAodFp.setContentDescription(mCanDismissLockScreen ? mUnlockedLabel : mLockedLabel);
-
-            mView.updateIcon(ICON_FINGERPRINT, true); // this shows no icon
+        } else if (mShowAodUnlockedIcon) {
+            mView.updateIcon(ICON_UNLOCK, true);
+            mView.setContentDescription(mUnlockedLabel);
             mView.setVisibility(View.VISIBLE);
         } else if (mShowAodLockIcon) {
-            if (wasShowingUnlock) {
-                // transition to the unlock icon first
-                mView.updateIcon(ICON_LOCK, false);
-            }
             mView.updateIcon(ICON_LOCK, true);
             mView.setContentDescription(mLockedLabel);
             mView.setVisibility(View.VISIBLE);
@@ -307,11 +290,6 @@
             mView.setContentDescription(null);
         }
 
-        if (!mShowAODFpIcon && mAodFp != null) {
-            mAodFp.setVisibility(View.INVISIBLE);
-            mAodFp.setContentDescription(null);
-        }
-
         if (!Objects.equals(prevContentDescription, mView.getContentDescription())
                 && mView.getContentDescription() != null) {
             mView.announceForAccessibility(mView.getContentDescription());
@@ -399,7 +377,7 @@
         pw.println();
         pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
         pw.println(" mShowLockIcon: " + mShowLockIcon);
-        pw.println(" mShowAODFpIcon: " + mShowAODFpIcon);
+        pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon);
         pw.println("  mIsDozing: " + mIsDozing);
         pw.println("  mIsBouncerShowing: " + mIsBouncerShowing);
         pw.println("  mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric);
@@ -428,13 +406,6 @@
                         - mMaxBurnInOffsetY, mInterpolatedDarkAmount);
         float progress = MathUtils.lerp(0f, getBurnInProgressOffset(), mInterpolatedDarkAmount);
 
-        if (mAodFp != null) {
-            mAodFp.setTranslationX(offsetX);
-            mAodFp.setTranslationY(offsetY);
-            mAodFp.setProgress(progress);
-            mAodFp.setAlpha(255 * mInterpolatedDarkAmount);
-        }
-
         mView.setTranslationX(offsetX);
         mView.setTranslationY(offsetY);
     }
@@ -447,10 +418,6 @@
         mView.setUseBackground(mUdfpsSupported);
 
         mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
-        if (!wasUdfpsEnrolled && mUdfpsEnrolled && mAodFp == null) {
-            mLayoutInflater.inflate(R.layout.udfps_aod_lock_icon, mView);
-            mAodFp = mView.findViewById(R.id.lock_udfps_aod_fp);
-        }
         if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) {
             updateVisibility();
         }
@@ -597,15 +564,11 @@
             case MotionEvent.ACTION_DOWN:
             case MotionEvent.ACTION_HOVER_ENTER:
                 if (mVibrator != null && !mDownDetected) {
-                    if (mTick == null) {
-                        mTick = UdfpsController.lowTick(getContext(), true,
-                                LONG_PRESS_TIMEOUT);
-                    }
                     mVibrator.vibrate(
                             Process.myUid(),
                             getContext().getOpPackageName(),
-                            mTick,
-                            "lock-icon-tick",
+                            UdfpsController.EFFECT_CLICK,
+                            "lock-icon-down",
                             VIBRATION_SONIFICATION_ATTRIBUTES);
                 }
 
@@ -718,8 +681,7 @@
 
     private boolean inLockIconArea(MotionEvent event) {
         return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
-                && (mView.getVisibility() == View.VISIBLE
-                || (mAodFp != null && mAodFp.getVisibility() == View.VISIBLE));
+                && mView.getVisibility() == View.VISIBLE;
     }
 
     private boolean isActionable() {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index bb0e79f..db88569 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -121,7 +121,7 @@
                     .setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
                     .setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
                     .setRecentTasks(mWMComponent.getRecentTasks())
-                    .setSizeCompatUI(Optional.of(mWMComponent.getSizeCompatUI()))
+                    .setCompatUI(Optional.of(mWMComponent.getCompatUI()))
                     .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()));
         } else {
             // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
@@ -141,7 +141,7 @@
                     .setStartingSurface(Optional.ofNullable(null))
                     .setTaskSurfaceHelper(Optional.ofNullable(null))
                     .setRecentTasks(Optional.ofNullable(null))
-                    .setSizeCompatUI(Optional.ofNullable(null))
+                    .setCompatUI(Optional.ofNullable(null))
                     .setDragAndDrop(Optional.ofNullable(null));
         }
         mSysUIComponent = builder.build();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index f11dc93..e35b558 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -636,6 +636,7 @@
         mIndicatorView.setText(message);
         mIndicatorView.setTextColor(mTextColorError);
         mIndicatorView.setVisibility(View.VISIBLE);
+        mIndicatorView.setSelected(true);
         mHandler.postDelayed(resetMessageRunnable, mInjector.getDelayAfterError());
 
         Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 1226dca..d8f6a01 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -31,6 +31,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.PointF;
+import android.hardware.SensorPrivacyManager;
 import android.hardware.biometrics.BiometricAuthenticator.Modality;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricManager.Authenticators;
@@ -89,6 +90,7 @@
 
     private static final String TAG = "AuthController";
     private static final boolean DEBUG = true;
+    private static final int SENSOR_PRIVACY_DELAY = 500;
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final CommandQueue mCommandQueue;
@@ -122,6 +124,7 @@
     @Nullable private List<FingerprintSensorPropertiesInternal> mSidefpsProps;
 
     @NonNull private final SparseBooleanArray mUdfpsEnrolledForUser;
+    private SensorPrivacyManager mSensorPrivacyManager;
 
     private class BiometricTaskStackListener extends TaskStackListener {
         @Override
@@ -492,6 +495,7 @@
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
 
         context.registerReceiver(mBroadcastReceiver, filter);
+        mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
     }
 
     private void updateFingerprintLocation() {
@@ -642,10 +646,16 @@
         final boolean isLockout = (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT)
                 || (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT);
 
+        boolean isCameraPrivacyEnabled = false;
+        if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE
+                && mSensorPrivacyManager.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA,
+                mCurrentDialogArgs.argi1 /* userId */)) {
+            isCameraPrivacyEnabled = true;
+        }
         // TODO(b/141025588): Create separate methods for handling hard and soft errors.
         final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED
-                || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT);
-
+                || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT
+                || isCameraPrivacyEnabled);
         if (mCurrentDialog != null) {
             if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) {
                 if (DEBUG) Log.d(TAG, "onBiometricError, lockout");
@@ -655,12 +665,23 @@
                         ? mContext.getString(R.string.biometric_not_recognized)
                         : getErrorString(modality, error, vendorCode);
                 if (DEBUG) Log.d(TAG, "onBiometricError, soft error: " + errorMessage);
-                mCurrentDialog.onAuthenticationFailed(modality, errorMessage);
+                // The camera privacy error can return before the prompt initializes its state,
+                // causing the prompt to appear to endlessly authenticate. Add a small delay
+                // to stop this.
+                if (isCameraPrivacyEnabled) {
+                    mHandler.postDelayed(() -> {
+                        mCurrentDialog.onAuthenticationFailed(modality,
+                                mContext.getString(R.string.face_sensor_privacy_enabled));
+                    }, SENSOR_PRIVACY_DELAY);
+                } else {
+                    mCurrentDialog.onAuthenticationFailed(modality, errorMessage);
+                }
             } else {
                 final String errorMessage = getErrorString(modality, error, vendorCode);
                 if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage);
                 mCurrentDialog.onError(modality, errorMessage);
             }
+
         } else {
             Log.w(TAG, "onBiometricError callback but dialog is gone");
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index fb4616a..07aec69 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -23,6 +23,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.util.ViewController;
@@ -44,6 +45,7 @@
         extends ViewController<T> implements Dumpable {
     @NonNull final StatusBarStateController mStatusBarStateController;
     @NonNull final PanelExpansionStateManager mPanelExpansionStateManager;
+    @NonNull final SystemUIDialogManager mDialogManager;
     @NonNull final DumpManager mDumpManger;
 
     boolean mNotificationShadeVisible;
@@ -52,10 +54,12 @@
             T view,
             @NonNull StatusBarStateController statusBarStateController,
             @NonNull PanelExpansionStateManager panelExpansionStateManager,
+            @NonNull SystemUIDialogManager dialogManager,
             @NonNull DumpManager dumpManager) {
         super(view);
         mStatusBarStateController = statusBarStateController;
         mPanelExpansionStateManager = panelExpansionStateManager;
+        mDialogManager = dialogManager;
         mDumpManger = dumpManager;
     }
 
@@ -64,12 +68,14 @@
     @Override
     protected void onViewAttached() {
         mPanelExpansionStateManager.addExpansionListener(mPanelExpansionListener);
+        mDialogManager.registerListener(mDialogListener);
         mDumpManger.registerDumpable(getDumpTag(), this);
     }
 
     @Override
     protected void onViewDetached() {
         mPanelExpansionStateManager.removeExpansionListener(mPanelExpansionListener);
+        mDialogManager.registerListener(mDialogListener);
         mDumpManger.unregisterDumpable(getDumpTag());
     }
 
@@ -95,7 +101,8 @@
      * authentication.
      */
     boolean shouldPauseAuth() {
-        return mNotificationShadeVisible;
+        return mNotificationShadeVisible
+                || mDialogManager.shouldHideAffordance();
     }
 
     /**
@@ -189,4 +196,7 @@
             updatePauseAuth();
         }
     };
+
+    private final SystemUIDialogManager.Listener mDialogListener =
+            (shouldHide) -> updatePauseAuth();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
index 894b295..3732100 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
@@ -20,6 +20,7 @@
 
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 
 /**
@@ -30,8 +31,10 @@
             @NonNull UdfpsBpView view,
             @NonNull StatusBarStateController statusBarStateController,
             @NonNull PanelExpansionStateManager panelExpansionStateManager,
+            @NonNull SystemUIDialogManager systemUIDialogManager,
             @NonNull DumpManager dumpManager) {
-        super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+        super(view, statusBarStateController, panelExpansionStateManager,
+                systemUIDialogManager, dumpManager);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 9808045..3e9d6b0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -47,7 +47,6 @@
 import android.os.Trace;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
-import android.provider.Settings;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -71,6 +70,7 @@
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -119,6 +119,7 @@
     @NonNull private final KeyguardStateController mKeyguardStateController;
     @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
     @NonNull private final DumpManager mDumpManager;
+    @NonNull private final SystemUIDialogManager mDialogManager;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Nullable private final Vibrator mVibrator;
     @NonNull private final FalsingManager mFalsingManager;
@@ -165,9 +166,6 @@
     private boolean mAttemptedToDismissKeyguard;
     private Set<Callback> mCallbacks = new HashSet<>();
 
-    private static final int DEFAULT_TICK = VibrationEffect.Composition.PRIMITIVE_LOW_TICK;
-    private final VibrationEffect mTick;
-
     @VisibleForTesting
     public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
             new AudioAttributes.Builder()
@@ -282,9 +280,6 @@
                     return;
                 }
                 mGoodCaptureReceived = true;
-                if (mVibrator != null) {
-                    mVibrator.cancel();
-                }
                 mView.stopIllumination();
                 if (mServerRequest != null) {
                     mServerRequest.onAcquiredGood();
@@ -559,7 +554,8 @@
             @Main Handler mainHandler,
             @NonNull ConfigurationController configurationController,
             @NonNull SystemClock systemClock,
-            @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
+            @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+            @NonNull SystemUIDialogManager dialogManager) {
         mContext = context;
         mExecution = execution;
         mVibrator = vibrator;
@@ -574,6 +570,7 @@
         mKeyguardStateController = keyguardStateController;
         mKeyguardViewManager = statusBarKeyguardViewManager;
         mDumpManager = dumpManager;
+        mDialogManager = dialogManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mFalsingManager = falsingManager;
         mPowerManager = powerManager;
@@ -586,7 +583,6 @@
         mConfigurationController = configurationController;
         mSystemClock = systemClock;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
-        mTick = lowTick(context, false /* useShortRampup */, DEFAULT_VIBRATION_DURATION);
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -622,47 +618,6 @@
     }
 
     /**
-     * Returns the continuous low tick effect that starts playing on the udfps finger-down event.
-     */
-    public static VibrationEffect lowTick(
-            Context context,
-            boolean useShortRampUp,
-            long duration
-    ) {
-        boolean useLowTickDefault = context.getResources()
-                .getBoolean(R.bool.config_udfpsUseLowTick);
-        int primitiveTick = DEFAULT_TICK;
-        if (Settings.Global.getFloat(
-                context.getContentResolver(),
-                "tick-low", useLowTickDefault ? 1 : 0) == 0) {
-            primitiveTick = VibrationEffect.Composition.PRIMITIVE_TICK;
-        }
-        float tickIntensity = Settings.Global.getFloat(
-                context.getContentResolver(),
-                "tick-intensity",
-                context.getResources().getFloat(R.dimen.config_udfpsTickIntensity));
-        int tickDelay = Settings.Global.getInt(
-                context.getContentResolver(),
-                "tick-delay",
-                context.getResources().getInteger(R.integer.config_udfpsTickDelay));
-
-        VibrationEffect.Composition composition = VibrationEffect.startComposition();
-        composition.addPrimitive(primitiveTick, tickIntensity, 0);
-        int primitives = (int) (duration / tickDelay);
-        float[] rampUp = new float[]{.48f, .58f, .69f, .83f};
-        if (useShortRampUp) {
-            rampUp = new float[]{.5f, .7f};
-        }
-        for (int i = 0; i < rampUp.length; i++) {
-            composition.addPrimitive(primitiveTick, tickIntensity * rampUp[i], tickDelay);
-        }
-        for (int i = rampUp.length; i < primitives; i++) {
-            composition.addPrimitive(primitiveTick, tickIntensity, tickDelay);
-        }
-        return composition.compose();
-    }
-
-    /**
      * Play haptic to signal udfps scanning started.
      */
     @VisibleForTesting
@@ -671,8 +626,8 @@
             mVibrator.vibrate(
                     Process.myUid(),
                     mContext.getOpPackageName(),
-                    mTick,
-                    "udfps-onStart-tick",
+                    EFFECT_CLICK,
+                    "udfps-onStart-click",
                     VIBRATION_SONIFICATION_ATTRIBUTES);
         }
     }
@@ -864,6 +819,7 @@
                         mServerRequest.mEnrollHelper,
                         mStatusBarStateController,
                         mPanelExpansionStateManager,
+                        mDialogManager,
                         mDumpManager
                 );
             case BiometricOverlayConstants.REASON_AUTH_KEYGUARD:
@@ -882,6 +838,7 @@
                         mSystemClock,
                         mKeyguardStateController,
                         mUnlockedScreenOffAnimationController,
+                        mDialogManager,
                         this
                 );
             case BiometricOverlayConstants.REASON_AUTH_BP:
@@ -892,6 +849,7 @@
                         bpView,
                         mStatusBarStateController,
                         mPanelExpansionStateManager,
+                        mDialogManager,
                         mDumpManager
                 );
             case BiometricOverlayConstants.REASON_AUTH_OTHER:
@@ -903,6 +861,7 @@
                         authOtherView,
                         mStatusBarStateController,
                         mPanelExpansionStateManager,
+                        mDialogManager,
                         mDumpManager
                 );
             default:
@@ -1059,7 +1018,6 @@
             }
         }
         mOnFingerDown = false;
-        mVibrator.cancel();
         if (mView.isIlluminationRequested()) {
             mView.stopIllumination();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index 1f01fc5..ac38b13 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -20,9 +20,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.content.res.TypedArray;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -30,7 +28,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Looper;
-import android.util.TypedValue;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.LinearInterpolator;
 
@@ -38,7 +35,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.internal.graphics.ColorUtils;
 import com.android.systemui.R;
 
 /**
@@ -106,9 +102,8 @@
 
         mSensorOutlinePaint = new Paint(0 /* flags */);
         mSensorOutlinePaint.setAntiAlias(true);
-        mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_enroll_icon));
-        mSensorOutlinePaint.setStyle(Paint.Style.STROKE);
-        mSensorOutlinePaint.setStrokeWidth(2.f);
+        mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_moving_target_fill));
+        mSensorOutlinePaint.setStyle(Paint.Style.FILL);
 
         mBlueFill = new Paint(0 /* flags */);
         mBlueFill.setAntiAlias(true);
@@ -117,12 +112,12 @@
 
         mMovingTargetFpIcon = context.getResources()
                 .getDrawable(R.drawable.ic_kg_fingerprint, null);
-        mMovingTargetFpIcon.setTint(Color.WHITE);
+        mMovingTargetFpIcon.setTint(mContext.getColor(R.color.udfps_enroll_icon));
         mMovingTargetFpIcon.mutate();
 
         mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
 
-        mHintColorFaded = getHintColorFaded(context);
+        mHintColorFaded = context.getColor(R.color.udfps_moving_target_fill);
         mHintColorHighlight = context.getColor(R.color.udfps_enroll_progress);
         mHintMaxWidthPx = Utils.dpToPixels(context, HINT_MAX_WIDTH_DP);
         mHintPaddingPx = Utils.dpToPixels(context, HINT_PADDING_DP);
@@ -218,22 +213,6 @@
         };
     }
 
-    @ColorInt
-    private static int getHintColorFaded(@NonNull Context context) {
-        final TypedValue tv = new TypedValue();
-        context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, tv, true);
-        final int alpha = (int) (tv.getFloat() * 255f);
-
-        final int[] attrs = new int[] {android.R.attr.colorControlNormal};
-        final TypedArray ta = context.obtainStyledAttributes(attrs);
-        try {
-            @ColorInt final int color = ta.getColor(0, context.getColor(R.color.white_disabled));
-            return ColorUtils.setAlphaComponent(color, alpha);
-        } finally {
-            ta.recycle();
-        }
-    }
-
     void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
         mEnrollHelper = helper;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index 79c7e66..631a461 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -18,12 +18,10 @@
 
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
-import android.util.TypedValue;
 import android.view.animation.Interpolator;
 import android.view.animation.OvershootInterpolator;
 
@@ -80,24 +78,11 @@
 
         mBackgroundPaint = new Paint();
         mBackgroundPaint.setStrokeWidth(mStrokeWidthPx);
-        mBackgroundPaint.setColor(context.getColor(R.color.white_disabled));
+        mBackgroundPaint.setColor(context.getColor(R.color.udfps_moving_target_fill));
         mBackgroundPaint.setAntiAlias(true);
         mBackgroundPaint.setStyle(Paint.Style.STROKE);
         mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
 
-        // Set background paint color and alpha.
-        final int[] attrs = new int[] {android.R.attr.colorControlNormal};
-        final TypedArray typedArray = context.obtainStyledAttributes(attrs);
-        try {
-            @ColorInt final int tintColor = typedArray.getColor(0, mBackgroundPaint.getColor());
-            mBackgroundPaint.setColor(tintColor);
-        } finally {
-            typedArray.recycle();
-        }
-        TypedValue alpha = new TypedValue();
-        context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, alpha, true);
-        mBackgroundPaint.setAlpha((int) (alpha.getFloat() * 255f));
-
         // Progress fill should *not* use the extracted system color.
         mFillPaint = new Paint();
         mFillPaint.setStrokeWidth(mStrokeWidthPx);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 292a904..ac9e92e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -22,6 +22,7 @@
 import com.android.systemui.R;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 
 /**
@@ -54,8 +55,10 @@
             @NonNull UdfpsEnrollHelper enrollHelper,
             @NonNull StatusBarStateController statusBarStateController,
             @NonNull PanelExpansionStateManager panelExpansionStateManager,
+            @NonNull SystemUIDialogManager systemUIDialogManager,
             @NonNull DumpManager dumpManager) {
-        super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+        super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
+                dumpManager);
         mEnrollProgressBarRadius = getContext().getResources()
                 .getInteger(R.integer.config_udfpsEnrollProgressBar);
         mEnrollHelper = enrollHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
index 6198733..97dca0f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
@@ -20,6 +20,7 @@
 
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 
 /**
@@ -33,8 +34,10 @@
             @NonNull UdfpsFpmOtherView view,
             @NonNull StatusBarStateController statusBarStateController,
             @NonNull PanelExpansionStateManager panelExpansionStateManager,
+            @NonNull SystemUIDialogManager systemUIDialogManager,
             @NonNull DumpManager dumpManager) {
-        super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+        super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
+                dumpManager);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 8f4d6f6..3e8a568 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -31,6 +31,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -86,8 +87,10 @@
             @NonNull SystemClock systemClock,
             @NonNull KeyguardStateController keyguardStateController,
             @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+            @NonNull SystemUIDialogManager systemUIDialogManager,
             @NonNull UdfpsController udfpsController) {
-        super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
+        super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
+                dumpManager);
         mKeyguardViewManager = statusBarKeyguardViewManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLockScreenShadeTransitionController = transitionController;
@@ -217,6 +220,10 @@
             return false;
         }
 
+        if (mDialogManager.shouldHideAffordance()) {
+            return true;
+        }
+
         if (mLaunchTransitionFadingAway) {
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index d3d6e03..6f30ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -18,6 +18,7 @@
 
 import android.annotation.MainThread
 import android.app.Dialog
+import android.app.PendingIntent
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
@@ -88,7 +89,7 @@
         bouncerOrRun(createAction(cvh.cws.ci.controlId, {
             cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
             if (cvh.usePanel()) {
-                showDetail(cvh, control.getAppIntent().getIntent())
+                showDetail(cvh, control.getAppIntent())
             } else {
                 cvh.action(CommandAction(templateId))
             }
@@ -116,7 +117,7 @@
             // Long press snould only be called when there is valid control state, otherwise ignore
             cvh.cws.control?.let {
                 cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
-                showDetail(cvh, it.getAppIntent().getIntent())
+                showDetail(cvh, it.getAppIntent())
             }
         }, false /* blockable */))
     }
@@ -167,10 +168,10 @@
         bgExecutor.execute { vibrator.vibrate(effect) }
     }
 
-    private fun showDetail(cvh: ControlViewHolder, intent: Intent) {
+    private fun showDetail(cvh: ControlViewHolder, pendingIntent: PendingIntent) {
         bgExecutor.execute {
             val activities: List<ResolveInfo> = context.packageManager.queryIntentActivities(
-                intent,
+                pendingIntent.getIntent(),
                 PackageManager.MATCH_DEFAULT_ONLY
             )
 
@@ -178,7 +179,7 @@
                 // make sure the intent is valid before attempting to open the dialog
                 if (activities.isNotEmpty() && taskViewFactory.isPresent) {
                     taskViewFactory.get().create(context, uiExecutor, {
-                        dialog = DetailDialog(activityContext, it, intent, cvh).also {
+                        dialog = DetailDialog(activityContext, it, pendingIntent, cvh).also {
                             it.setOnDismissListener { _ -> dialog = null }
                             it.show()
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 8a47a36..4758ab0 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -43,7 +43,7 @@
 class DetailDialog(
     val activityContext: Context?,
     val taskView: TaskView,
-    val intent: Intent,
+    val pendingIntent: PendingIntent,
     val cvh: ControlViewHolder
 ) : Dialog(
     activityContext ?: cvh.context,
@@ -59,6 +59,14 @@
 
     var detailTaskId = INVALID_TASK_ID
 
+    private val fillInIntent = Intent().apply {
+        putExtra(EXTRA_USE_PANEL, true)
+
+        // Apply flags to make behaviour match documentLaunchMode=always.
+        addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
+        addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+    }
+
     fun removeDetailTask() {
         if (detailTaskId == INVALID_TASK_ID) return
         ActivityTaskManager.getInstance().removeTask(detailTaskId)
@@ -67,13 +75,6 @@
 
     val stateCallback = object : TaskView.Listener {
         override fun onInitialized() {
-            val launchIntent = Intent(intent)
-            launchIntent.putExtra(EXTRA_USE_PANEL, true)
-
-            // Apply flags to make behaviour match documentLaunchMode=always.
-            launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
-            launchIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
-
             val options = activityContext?.let {
                 ActivityOptions.makeCustomAnimation(
                     it,
@@ -82,9 +83,8 @@
                 )
             } ?: ActivityOptions.makeBasic()
             taskView.startActivity(
-                PendingIntent.getActivity(context, 0, launchIntent,
-                        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE),
-                null /* fillInIntent */,
+                pendingIntent,
+                fillInIntent,
                 options,
                 getTaskViewBounds()
             )
@@ -97,6 +97,9 @@
 
         override fun onTaskCreated(taskId: Int, name: ComponentName?) {
             detailTaskId = taskId
+            requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
+                setAlpha(1f)
+            }
         }
 
         override fun onReleased() {
@@ -121,6 +124,7 @@
 
         requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
             addView(taskView)
+            setAlpha(0f)
         }
 
         requireViewById<ImageView>(R.id.control_detail_close).apply {
@@ -134,7 +138,7 @@
                 removeDetailTask()
                 dismiss()
                 context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
-                v.context.startActivity(intent)
+                pendingIntent.send()
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index c5d3a70..d950d6d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -31,6 +31,7 @@
 import com.android.wm.shell.TaskViewFactory;
 import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
 import com.android.wm.shell.draganddrop.DragAndDrop;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
@@ -38,7 +39,6 @@
 import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.recents.RecentTasks;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.startingsurface.StartingSurface;
 import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
@@ -110,7 +110,7 @@
         Builder setRecentTasks(Optional<RecentTasks> r);
 
         @BindsInstance
-        Builder setSizeCompatUI(Optional<SizeCompatUI> s);
+        Builder setCompatUI(Optional<CompatUI> s);
 
         @BindsInstance
         Builder setDragAndDrop(Optional<DragAndDrop> d);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 90a3ad2..b815d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -25,6 +25,7 @@
 import com.android.wm.shell.TaskViewFactory;
 import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.dagger.TvWMShellModule;
 import com.android.wm.shell.dagger.WMShellModule;
 import com.android.wm.shell.dagger.WMSingleton;
@@ -35,7 +36,6 @@
 import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.recents.RecentTasks;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.startingsurface.StartingSurface;
 import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
@@ -119,7 +119,7 @@
     Optional<RecentTasks> getRecentTasks();
 
     @WMSingleton
-    SizeCompatUI getSizeCompatUI();
+    CompatUI getCompatUI();
 
     @WMSingleton
     DragAndDrop getDragAndDrop();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index ff14064..2ebcd853 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -121,6 +121,7 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.telephony.TelephonyListenerManager;
@@ -236,6 +237,7 @@
     protected Handler mMainHandler;
     private int mSmallestScreenWidthDp;
     private final Optional<StatusBar> mStatusBarOptional;
+    private final SystemUIDialogManager mDialogManager;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final DialogLaunchAnimator mDialogLaunchAnimator;
 
@@ -346,7 +348,8 @@
             PackageManager packageManager,
             Optional<StatusBar> statusBarOptional,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            DialogLaunchAnimator dialogLaunchAnimator) {
+            DialogLaunchAnimator dialogLaunchAnimator,
+            SystemUIDialogManager dialogManager) {
         mContext = context;
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
@@ -378,6 +381,7 @@
         mStatusBarOptional = statusBarOptional;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mDialogLaunchAnimator = dialogLaunchAnimator;
+        mDialogManager = dialogManager;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -677,7 +681,8 @@
                 mAdapter, mOverflowAdapter, mSysuiColorExtractor,
                 mStatusBarService, mNotificationShadeWindowController,
                 mSysUiState, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
-                mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils);
+                mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils,
+                mDialogManager);
 
         dialog.setOnDismissListener(this);
         dialog.setOnShowListener(this);
@@ -2219,10 +2224,12 @@
                 SysUiState sysuiState, Runnable onRefreshCallback, boolean keyguardShowing,
                 MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
                 Optional<StatusBar> statusBarOptional,
-                KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
+                KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils,
+                SystemUIDialogManager systemUiDialogManager) {
             // We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
             // dismiss this dialog when the device is locked.
-            super(context, themeRes, false /* dismissOnDeviceLock */);
+            super(context, themeRes, false /* dismissOnDeviceLock */,
+                    systemUiDialogManager);
             mContext = context;
             mAdapter = adapter;
             mOverflowAdapter = overflowAdapter;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8d07336..89a5d72 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2090,6 +2090,15 @@
     private final Runnable mKeyguardGoingAwayRunnable = new Runnable() {
         @Override
         public void run() {
+            // If the keyguard is already going away, or it's about to because we are going to
+            // trigger the going-away remote animation to show the surface behind, don't do it
+            // again. That will cause the current animation to be cancelled unnecessarily.
+            if (mKeyguardStateController.isKeyguardGoingAway()
+                    || mSurfaceBehindRemoteAnimationRequested
+                    || mSurfaceBehindRemoteAnimationRunning) {
+                return;
+            }
+
             Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable");
             if (DEBUG) Log.d(TAG, "keyguardGoingAway");
             mKeyguardViewControllerLazy.get().keyguardGoingAway();
@@ -2451,9 +2460,7 @@
 
         if (mSurfaceBehindRemoteAnimationFinishedCallback != null) {
             try {
-                if (!cancelled) {
-                    mSurfaceBehindRemoteAnimationFinishedCallback.onAnimationFinished();
-                }
+                mSurfaceBehindRemoteAnimationFinishedCallback.onAnimationFinished();
                 mSurfaceBehindRemoteAnimationFinishedCallback = null;
             } catch (RemoteException e) {
                 e.printStackTrace();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index c12d48d..f2cb254 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -44,6 +44,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 /**
  * Base dialog for media output UI
@@ -52,6 +53,7 @@
         MediaOutputController.Callback, Window.Callback {
 
     private static final String TAG = "MediaOutputDialog";
+    private static final String EMPTY_TITLE = " ";
 
     private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
     private final RecyclerView.LayoutManager mLayoutManager;
@@ -82,9 +84,12 @@
         }
     };
 
-    public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) {
-        super(context);
-        mContext = context;
+    public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController,
+            SystemUIDialogManager dialogManager) {
+        super(context, dialogManager);
+
+        // Save the context that is wrapped with our theme.
+        mContext = getContext();
         mMediaOutputController = mediaOutputController;
         mLayoutManager = new LinearLayoutManager(mContext);
         mListMaxHeight = context.getResources().getDimensionPixelSize(
@@ -105,6 +110,9 @@
         lp.setFitInsetsIgnoringVisibility(true);
         window.setAttributes(lp);
         window.setContentView(mDialogView);
+        // Sets window to a blank string to avoid talkback announce app label first when pop up,
+        // which doesn't make sense.
+        window.setTitle(EMPTY_TITLE);
 
         mHeaderTitle = mDialogView.requireViewById(R.id.header_title);
         mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 6da4d48..a1e2c57 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.media.dialog;
 
+import static android.provider.Settings.ACTION_BLUETOOTH_PAIRING_SETTINGS;
+
 import android.app.Notification;
 import android.content.Context;
 import android.content.Intent;
@@ -30,6 +32,7 @@
 import android.media.session.PlaybackState;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
@@ -46,7 +49,6 @@
 import com.android.settingslib.media.InfoMediaManager;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
-import com.android.settingslib.media.MediaOutputConstants;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.R;
 import com.android.systemui.animation.DialogLaunchAnimator;
@@ -54,6 +56,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -69,7 +72,8 @@
 
     private static final String TAG = "MediaOutputController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
+    private static final String PAGE_CONNECTED_DEVICES_KEY =
+            "top_level_connected_devices";
     private final String mPackageName;
     private final Context mContext;
     private final MediaSessionManager mMediaSessionManager;
@@ -77,6 +81,7 @@
     private final ShadeController mShadeController;
     private final ActivityStarter mActivityStarter;
     private final DialogLaunchAnimator mDialogLaunchAnimator;
+    private final SystemUIDialogManager mDialogManager;
     private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
     private final boolean mAboveStatusbar;
     private final boolean mVolumeAdjustmentForRemoteGroupSessions;
@@ -98,7 +103,7 @@
             boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
             lbm, ShadeController shadeController, ActivityStarter starter,
             NotificationEntryManager notificationEntryManager, UiEventLogger uiEventLogger,
-            DialogLaunchAnimator dialogLaunchAnimator) {
+            DialogLaunchAnimator dialogLaunchAnimator, SystemUIDialogManager dialogManager) {
         mContext = context;
         mPackageName = packageName;
         mMediaSessionManager = mediaSessionManager;
@@ -114,6 +119,7 @@
         mDialogLaunchAnimator = dialogLaunchAnimator;
         mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
+        mDialogManager = dialogManager;
     }
 
     void start(@NonNull Callback cb) {
@@ -451,23 +457,34 @@
         mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
 
         mCallback.dismissDialog();
-        final ActivityStarter.OnDismissAction postKeyguardAction = () -> {
-            mContext.sendBroadcast(new Intent()
-                    .setAction(MediaOutputConstants.ACTION_LAUNCH_BLUETOOTH_PAIRING)
-                    .setPackage(MediaOutputConstants.SETTINGS_PACKAGE_NAME));
-            mShadeController.animateCollapsePanels();
-            return true;
-        };
-        mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true);
+        Intent launchIntent =
+                new Intent(ACTION_BLUETOOTH_PAIRING_SETTINGS)
+                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        final Intent deepLinkIntent =
+                new Intent(Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY);
+        if (deepLinkIntent.resolveActivity(mContext.getPackageManager()) != null) {
+            Log.d(TAG, "Device support split mode, launch page with deep link");
+            deepLinkIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            deepLinkIntent.putExtra(
+                    Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI,
+                    launchIntent.toUri(Intent.URI_INTENT_SCHEME));
+            deepLinkIntent.putExtra(
+                    Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY,
+                    PAGE_CONNECTED_DEVICES_KEY);
+            mActivityStarter.startActivity(deepLinkIntent, true);
+            return;
+        }
+        mActivityStarter.startActivity(launchIntent, true);
     }
 
     void launchMediaOutputGroupDialog(View mediaOutputDialog) {
         // We show the output group dialog from the output dialog.
         MediaOutputController controller = new MediaOutputController(mContext, mPackageName,
                 mAboveStatusbar, mMediaSessionManager, mLocalBluetoothManager, mShadeController,
-                mActivityStarter, mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mActivityStarter, mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator,
+                mDialogManager);
         MediaOutputGroupDialog dialog = new MediaOutputGroupDialog(mContext, mAboveStatusbar,
-                controller);
+                controller, mDialogManager);
         mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index eca8ac9..7e2558c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -28,6 +28,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 /**
  * Dialog for media output transferring.
@@ -37,8 +38,9 @@
     final UiEventLogger mUiEventLogger;
 
     MediaOutputDialog(Context context, boolean aboveStatusbar, MediaOutputController
-            mediaOutputController, UiEventLogger uiEventLogger) {
-        super(context, mediaOutputController);
+            mediaOutputController, UiEventLogger uiEventLogger,
+            SystemUIDialogManager dialogManager) {
+        super(context, mediaOutputController, dialogManager);
         mUiEventLogger = uiEventLogger;
         mAdapter = new MediaOutputAdapter(mMediaOutputController, this);
         if (!aboveStatusbar) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index b91901d..a7bc852 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.notification.NotificationEntryManager
 import com.android.systemui.statusbar.phone.ShadeController
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import javax.inject.Inject
 
 /**
@@ -38,7 +39,8 @@
     private val starter: ActivityStarter,
     private val notificationEntryManager: NotificationEntryManager,
     private val uiEventLogger: UiEventLogger,
-    private val dialogLaunchAnimator: DialogLaunchAnimator
+    private val dialogLaunchAnimator: DialogLaunchAnimator,
+    private val dialogManager: SystemUIDialogManager
 ) {
     companion object {
         var mediaOutputDialog: MediaOutputDialog? = null
@@ -51,8 +53,9 @@
 
         val controller = MediaOutputController(context, packageName, aboveStatusBar,
             mediaSessionManager, lbm, shadeController, starter, notificationEntryManager,
-            uiEventLogger, dialogLaunchAnimator)
-        val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger)
+            uiEventLogger, dialogLaunchAnimator, dialogManager)
+        val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger,
+                dialogManager)
         mediaOutputDialog = dialog
 
         // Show the dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
index b41e813..478b890 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
@@ -25,6 +25,7 @@
 import androidx.core.graphics.drawable.IconCompat;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 /**
  * Dialog for media output group.
@@ -32,8 +33,8 @@
 public class MediaOutputGroupDialog extends MediaOutputBaseDialog {
 
     MediaOutputGroupDialog(Context context, boolean aboveStatusbar, MediaOutputController
-            mediaOutputController) {
-        super(context, mediaOutputController);
+            mediaOutputController, SystemUIDialogManager dialogManager) {
+        super(context, mediaOutputController, dialogManager);
         mMediaOutputController.resetGroupMediaDevices();
         mAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
         if (!aboveStatusbar) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 03bceac..963576b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -628,7 +628,7 @@
         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
 
         mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
-        mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
+        mPipOptional.ifPresent(mNavigationBarView::addPipExclusionBoundsChangeListener);
 
         prepareNavigationBarView();
         checkNavBarModes();
@@ -699,6 +699,7 @@
         mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
         mHandler.removeCallbacks(mEnableLayoutTransitions);
         mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+        mPipOptional.ifPresent(mNavigationBarView::removePipExclusionBoundsChangeListener);
         mFrame = null;
         mNavigationBarView = null;
         mOrientationHandle = null;
@@ -1252,6 +1253,7 @@
     private void onImeSwitcherClick(View v) {
         mInputMethodManager.showInputMethodPickerFromSystem(
                 true /* showAuxiliarySubtypes */, mDisplayId);
+        mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
     };
 
     private boolean onLongPressBackHome(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index bfabf71..a984974 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -59,9 +59,11 @@
 import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.pip.Pip;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -106,7 +108,8 @@
             NavigationBar.Factory navigationBarFactory,
             DumpManager dumpManager,
             AutoHideController autoHideController,
-            LightBarController lightBarController) {
+            LightBarController lightBarController,
+            Optional<Pip> pipOptional) {
         mContext = context;
         mHandler = mainHandler;
         mNavigationBarFactory = navigationBarFactory;
@@ -118,7 +121,7 @@
         mTaskbarDelegate = taskbarDelegate;
         mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
                 navBarHelper, navigationModeController, sysUiFlagsContainer,
-                dumpManager, autoHideController, lightBarController);
+                dumpManager, autoHideController, lightBarController, pipOptional);
         mIsTablet = isTablet(mContext);
         dumpManager.registerDumpable(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 7c8c3e0..7adb7ac 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -75,12 +75,12 @@
 import com.android.systemui.navigationbar.buttons.NearestTouchFrame;
 import com.android.systemui.navigationbar.buttons.RotationContextButton;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
-import com.android.systemui.shared.rotation.FloatingRotationButton;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
+import com.android.systemui.shared.rotation.FloatingRotationButton;
 import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
 import com.android.systemui.shared.rotation.RotationButtonController;
-import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
@@ -1386,8 +1386,12 @@
         legacySplitScreen.registerInSplitScreenListener(mDockedListener);
     }
 
-    void registerPipExclusionBoundsChangeListener(Pip pip) {
-        pip.setPipExclusionBoundsChangeListener(mPipListener);
+    void addPipExclusionBoundsChangeListener(Pip pip) {
+        pip.addPipExclusionBoundsChangeListener(mPipListener);
+    }
+
+    void removePipExclusionBoundsChangeListener(Pip pip) {
+        pip.removePipExclusionBoundsChangeListener(mPipListener);
     }
 
     private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 68f4aea..feda99f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -40,6 +40,7 @@
 import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.inputmethodservice.InputMethodService;
 import android.os.IBinder;
@@ -68,9 +69,12 @@
 import com.android.systemui.statusbar.phone.BarTransitions;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
+import com.android.wm.shell.pip.Pip;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Optional;
+import java.util.function.Consumer;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -91,6 +95,7 @@
     private AutoHideController mAutoHideController;
     private LightBarController mLightBarController;
     private LightBarTransitionsController mLightBarTransitionsController;
+    private Optional<Pip> mPipOptional;
     private int mDisplayId;
     private int mNavigationIconHints;
     private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
@@ -113,6 +118,7 @@
     private Context mWindowContext;
     private ScreenPinningNotify mScreenPinningNotify;
     private int mNavigationMode;
+    private final Consumer<Rect> mPipListener;
 
     /**
      * Tracks the system calls for when taskbar should transiently show or hide so we can return
@@ -143,6 +149,7 @@
                 .create(context);
         mContext = context;
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
+        mPipListener = mEdgeBackGestureHandler::setPipStashExclusionBounds;
     }
 
     public void setDependencies(CommandQueue commandQueue,
@@ -151,7 +158,8 @@
             NavigationModeController navigationModeController,
             SysUiState sysUiState, DumpManager dumpManager,
             AutoHideController autoHideController,
-            LightBarController lightBarController) {
+            LightBarController lightBarController,
+            Optional<Pip> pipOptional) {
         // TODO: adding this in the ctor results in a dagger dependency cycle :(
         mCommandQueue = commandQueue;
         mOverviewProxyService = overviewProxyService;
@@ -162,6 +170,7 @@
         mAutoHideController = autoHideController;
         mLightBarController = lightBarController;
         mLightBarTransitionsController = createLightBarTransitionsController();
+        mPipOptional = pipOptional;
     }
 
     // Separated into a method to keep setDependencies() clean/readable.
@@ -207,6 +216,7 @@
         updateSysuiFlags();
         mAutoHideController.setNavigationBar(mAutoHideUiElement);
         mLightBarController.setNavigationBar(mLightBarTransitionsController);
+        mPipOptional.ifPresent(this::addPipExclusionBoundsChangeListener);
         mInitialized = true;
     }
 
@@ -228,9 +238,18 @@
         mAutoHideController.setNavigationBar(null);
         mLightBarTransitionsController.destroy(mContext);
         mLightBarController.setNavigationBar(null);
+        mPipOptional.ifPresent(this::removePipExclusionBoundsChangeListener);
         mInitialized = false;
     }
 
+    void addPipExclusionBoundsChangeListener(Pip pip) {
+        pip.addPipExclusionBoundsChangeListener(mPipListener);
+    }
+
+    void removePipExclusionBoundsChangeListener(Pip pip) {
+        pip.removePipExclusionBoundsChangeListener(mPipListener);
+    }
+
     /**
      * Returns {@code true} if this taskBar is {@link #init(int)}. Returns {@code false} if this
      * taskbar has not yet been {@link #init(int)} or has been {@link #destroy()}.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index debd2eb..d27b716 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -96,6 +96,9 @@
         @UiEvent(doc = "The overview button was pressed in the navigation bar.")
         NAVBAR_OVERVIEW_BUTTON_TAP(535),
 
+        @UiEvent(doc = "The ime switcher button was pressed in the navigation bar.")
+        NAVBAR_IME_SWITCHER_BUTTON_TAP(923),
+
         @UiEvent(doc = "The home button was long-pressed in the navigation bar.")
         NAVBAR_HOME_BUTTON_LONGPRESS(536),
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 7ba9cc2..a2577d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -98,7 +98,7 @@
                         toggleDataSaver();
                         Prefs.putBoolean(mContext, Prefs.Key.QS_DATA_SAVER_DIALOG_SHOWN, true);
                     });
-            dialog.setNegativeButton(com.android.internal.R.string.cancel, null);
+            dialog.setNeutralButton(com.android.internal.R.string.cancel, null);
             dialog.setShowForAllUsers(true);
 
             if (view != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 20805a1..a339dcf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -190,7 +190,6 @@
                 case Settings.Secure.ZEN_DURATION_PROMPT:
                     mUiHandler.post(() -> {
                         Dialog dialog = makeZenModeDialog();
-                        SystemUIDialog.registerDismissListener(dialog);
                         if (view != null) {
                             mDialogLaunchAnimator.showFromView(dialog, view, false);
                         } else {
@@ -211,10 +210,12 @@
     }
 
     private Dialog makeZenModeDialog() {
-        AlertDialog dialog = new EnableZenModeDialog(mContext, R.style.Theme_SystemUI_Dialog)
-                .createDialog();
+        AlertDialog dialog = new EnableZenModeDialog(mContext, R.style.Theme_SystemUI_Dialog,
+                true /* cancelIsNeutral */).createDialog();
         SystemUIDialog.applyFlags(dialog);
         SystemUIDialog.setShowForAllUsers(dialog, true);
+        SystemUIDialog.registerDismissListener(dialog);
+        SystemUIDialog.setDialogSize(dialog);
         return dialog;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 99eb5b6..544246e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -53,7 +53,10 @@
 
     private final InternetDialogController mInternetDialogController;
     private List<WifiEntry> mWifiEntries;
-    private int mWifiEntriesCount;
+    @VisibleForTesting
+    protected int mWifiEntriesCount;
+    @VisibleForTesting
+    protected int mMaxEntriesCount = InternetDialogController.MAX_WIFI_ENTRY_COUNT;
 
     protected View mHolderView;
     protected Context mContext;
@@ -87,7 +90,8 @@
      */
     public void setWifiEntries(@Nullable List<WifiEntry> wifiEntries, int wifiEntriesCount) {
         mWifiEntries = wifiEntries;
-        mWifiEntriesCount = wifiEntriesCount;
+        mWifiEntriesCount =
+                (wifiEntriesCount < mMaxEntriesCount) ? wifiEntriesCount : mMaxEntriesCount;
     }
 
     /**
@@ -101,6 +105,20 @@
     }
 
     /**
+     * Sets the maximum number of Wi-Fi networks.
+     */
+    public void setMaxEntriesCount(int count) {
+        if (count < 0 || mMaxEntriesCount == count) {
+            return;
+        }
+        mMaxEntriesCount = count;
+        if (mWifiEntriesCount > count) {
+            mWifiEntriesCount = count;
+            notifyDataSetChanged();
+        }
+    }
+
+    /**
      * ViewHolder for binding Wi-Fi view.
      */
     static class InternetViewHolder extends RecyclerView.ViewHolder {
@@ -133,7 +151,8 @@
         }
 
         void onBind(@NonNull WifiEntry wifiEntry) {
-            mWifiIcon.setImageDrawable(getWifiDrawable(wifiEntry));
+            mWifiIcon.setImageDrawable(
+                    getWifiDrawable(wifiEntry.getLevel(), wifiEntry.shouldShowXLevelIcon()));
             setWifiNetworkLayout(wifiEntry.getTitle(),
                     Html.fromHtml(wifiEntry.getSummary(false), Html.FROM_HTML_MODE_LEGACY));
 
@@ -170,12 +189,13 @@
             mWifiSummaryText.setText(summary);
         }
 
-        Drawable getWifiDrawable(@NonNull WifiEntry wifiEntry) {
-            if (wifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
+        Drawable getWifiDrawable(int level, boolean hasNoInternet) {
+            // If the Wi-Fi level is equal to WIFI_LEVEL_UNREACHABLE(-1), then a null drawable
+            // will be returned.
+            if (level == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
                 return null;
             }
-            final Drawable drawable = mWifiIconInjector.getIcon(wifiEntry.shouldShowXLevelIcon(),
-                    wifiEntry.getLevel());
+            final Drawable drawable = mWifiIconInjector.getIcon(hasNoInternet, level);
             if (drawable == null) {
                 return null;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 26c89ff..41ff56a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -16,6 +16,7 @@
 package com.android.systemui.qs.tiles.dialog;
 
 import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
 
 import android.app.AlertDialog;
 import android.content.Context;
@@ -78,7 +79,8 @@
     private static final String TAG = "InternetDialog";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    static final long PROGRESS_DELAY_MS = 2000L;
+    static final long PROGRESS_DELAY_MS = 1500L;
+    static final int MAX_NETWORK_COUNT = 4;
 
     private final Handler mHandler;
     private final Executor mBackgroundExecutor;
@@ -137,6 +139,8 @@
     protected WifiEntry mConnectedWifiEntry;
     @VisibleForTesting
     protected int mWifiEntriesCount;
+    @VisibleForTesting
+    protected boolean mHasMoreWifiEntries;
 
     // Wi-Fi scanning progress bar
     protected boolean mIsProgressBarVisible;
@@ -157,7 +161,9 @@
         if (DEBUG) {
             Log.d(TAG, "Init InternetDialog");
         }
-        mContext = context;
+
+        // Save the context that is wrapped with our theme.
+        mContext = getContext();
         mHandler = handler;
         mBackgroundExecutor = executor;
         mInternetDialogFactory = internetDialogFactory;
@@ -299,15 +305,11 @@
         if (DEBUG) {
             Log.d(TAG, "updateDialog");
         }
-        if (mInternetDialogController.isAirplaneModeEnabled()) {
-            mInternetDialogSubTitle.setVisibility(View.GONE);
-            mAirplaneModeLayout.setVisibility(View.VISIBLE);
-        } else {
-            mInternetDialogTitle.setText(getDialogTitleText());
-            mInternetDialogSubTitle.setVisibility(View.VISIBLE);
-            mInternetDialogSubTitle.setText(getSubtitleText());
-            mAirplaneModeLayout.setVisibility(View.GONE);
-        }
+        mInternetDialogTitle.setText(getDialogTitleText());
+        mInternetDialogSubTitle.setText(getSubtitleText());
+        mAirplaneModeLayout.setVisibility(
+                mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
+
         updateEthernet();
         if (shouldUpdateMobileNetwork) {
             setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular(),
@@ -462,22 +464,36 @@
             mSeeAllLayout.setVisibility(View.GONE);
             return;
         }
-        mWifiRecyclerView.setMinimumHeight(mWifiNetworkHeight * getWifiListMaxCount());
+        final int wifiListMaxCount = getWifiListMaxCount();
+        if (mAdapter.getItemCount() > wifiListMaxCount) {
+            mHasMoreWifiEntries = true;
+        }
+        mAdapter.setMaxEntriesCount(wifiListMaxCount);
+        final int wifiListMinHeight = mWifiNetworkHeight * wifiListMaxCount;
+        if (mWifiRecyclerView.getMinimumHeight() != wifiListMinHeight) {
+            mWifiRecyclerView.setMinimumHeight(wifiListMinHeight);
+        }
         mWifiRecyclerView.setVisibility(View.VISIBLE);
-        final boolean showSeeAll = mConnectedWifiEntry != null || mWifiEntriesCount > 0;
-        mSeeAllLayout.setVisibility(showSeeAll ? View.VISIBLE : View.INVISIBLE);
+        mSeeAllLayout.setVisibility(mHasMoreWifiEntries ? View.VISIBLE : View.INVISIBLE);
     }
 
     @VisibleForTesting
     @MainThread
     int getWifiListMaxCount() {
-        int count = InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+        // Use the maximum count of networks to calculate the remaining count for Wi-Fi networks.
+        int count = MAX_NETWORK_COUNT;
         if (mEthernetLayout.getVisibility() == View.VISIBLE) {
             count -= 1;
         }
         if (mMobileNetworkLayout.getVisibility() == View.VISIBLE) {
             count -= 1;
         }
+
+        // If the remaining count is greater than the maximum count of the Wi-Fi network, the
+        // maximum count of the Wi-Fi network is used.
+        if (count > MAX_WIFI_ENTRY_COUNT) {
+            count = MAX_WIFI_ENTRY_COUNT;
+        }
         if (mConnectedWifListLayout.getVisibility() == View.VISIBLE) {
             count -= 1;
         }
@@ -549,9 +565,13 @@
     }
 
     private void setProgressBarVisible(boolean visible) {
+        if (mIsProgressBarVisible == visible) {
+            return;
+        }
         mIsProgressBarVisible = visible;
-        mProgressBar.setVisibility(mIsProgressBarVisible ? View.VISIBLE : View.GONE);
-        mDivider.setVisibility(mIsProgressBarVisible ? View.GONE : View.VISIBLE);
+        mProgressBar.setVisibility(visible ? View.VISIBLE : View.GONE);
+        mProgressBar.setIndeterminate(visible);
+        mDivider.setVisibility(visible ? View.GONE : View.VISIBLE);
         mInternetDialogSubTitle.setText(getSubtitleText());
     }
 
@@ -651,13 +671,14 @@
     @Override
     @WorkerThread
     public void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
-            @Nullable WifiEntry connectedEntry) {
+            @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries) {
         // Should update the carrier network layout when it is connected under airplane mode ON.
         boolean shouldUpdateCarrierNetwork = mMobileNetworkLayout.getVisibility() == View.VISIBLE
                 && mInternetDialogController.isAirplaneModeEnabled();
         mHandler.post(() -> {
             mConnectedWifiEntry = connectedEntry;
             mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
+            mHasMoreWifiEntries = hasMoreWifiEntries;
             updateDialog(shouldUpdateCarrierNetwork /* shouldUpdateMobileNetwork */);
             mAdapter.setWifiEntries(wifiEntries, mWifiEntriesCount);
             mAdapter.notifyDataSetChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 6f63a08..0d064af 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -18,6 +18,7 @@
 
 import static com.android.settingslib.mobile.MobileMappings.getIconKey;
 import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
+import static com.android.wifitrackerlib.WifiEntry.CONNECTED_STATE_CONNECTED;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -125,7 +126,7 @@
             R.string.all_network_unavailable;
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    static final int MAX_WIFI_ENTRY_COUNT = 4;
+    static final int MAX_WIFI_ENTRY_COUNT = 3;
 
     private WifiManager mWifiManager;
     private Context mContext;
@@ -143,8 +144,6 @@
     private AccessPointController mAccessPointController;
     private IntentFilter mConnectionStateFilter;
     private InternetDialogCallback mCallback;
-    private WifiEntry mConnectedEntry;
-    private int mWifiEntriesCount;
     private UiEventLogger mUiEventLogger;
     private BroadcastDispatcher mBroadcastDispatcher;
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -156,6 +155,7 @@
     private SignalDrawable mSignalDrawable;
     private LocationController mLocationController;
     private DialogLaunchAnimator mDialogLaunchAnimator;
+    private boolean mHasWifiEntries;
 
     @VisibleForTesting
     static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
@@ -177,6 +177,8 @@
     protected KeyguardStateController mKeyguardStateController;
     @VisibleForTesting
     protected boolean mHasEthernet = false;
+    @VisibleForTesting
+    protected ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor;
 
     private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
             new KeyguardUpdateMonitorCallback() {
@@ -237,6 +239,7 @@
         mSignalDrawable = new SignalDrawable(mContext);
         mLocationController = locationController;
         mDialogLaunchAnimator = dialogLaunchAnimator;
+        mConnectedWifiInternetMonitor = new ConnectedWifiInternetMonitor();
     }
 
     void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
@@ -277,6 +280,7 @@
         mAccessPointController.removeAccessPointCallback(this);
         mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
         mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback);
+        mConnectedWifiInternetMonitor.unregisterCallback();
     }
 
     @VisibleForTesting
@@ -316,15 +320,11 @@
     }
 
     CharSequence getSubtitleText(boolean isProgressBarVisible) {
-        if (isAirplaneModeEnabled()) {
-            return null;
-        }
-
         if (mCanConfigWifi && !mWifiManager.isWifiEnabled()) {
-            // When the airplane mode is off and Wi-Fi is disabled.
+            // When Wi-Fi is disabled.
             //   Sub-Title: Wi-Fi is off
             if (DEBUG) {
-                Log.d(TAG, "Airplane mode off + Wi-Fi off.");
+                Log.d(TAG, "Wi-Fi off.");
             }
             return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF);
         }
@@ -338,7 +338,7 @@
             return mContext.getText(SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS);
         }
 
-        if (mConnectedEntry != null || mWifiEntriesCount > 0) {
+        if (mHasWifiEntries) {
             return mCanConfigWifi ? mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT) : null;
         }
 
@@ -348,6 +348,10 @@
             return mContext.getText(SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS);
         }
 
+        if (isCarrierNetworkActive()) {
+            return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
+        }
+
         // Sub-Title:
         // show non_carrier_network_unavailable
         //   - while Wi-Fi on + no Wi-Fi item
@@ -875,42 +879,30 @@
             return;
         }
 
-        if (accessPoints == null || accessPoints.size() == 0) {
-            mConnectedEntry = null;
-            mWifiEntriesCount = 0;
-            if (mCallback != null) {
-                mCallback.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */);
-            }
-            return;
-        }
-
-        int count = MAX_WIFI_ENTRY_COUNT;
-        if (mHasEthernet) {
-            count -= 1;
-        }
-        if (hasActiveSubId()) {
-            count -= 1;
-        }
-        if (count > accessPoints.size()) {
-            count = accessPoints.size();
-        }
-
         WifiEntry connectedEntry = null;
-        final List<WifiEntry> wifiEntries = new ArrayList<>();
-        for (int i = 0; i < count; i++) {
-            WifiEntry entry = accessPoints.get(i);
-            if (connectedEntry == null && entry.isDefaultNetwork() && entry.hasInternetAccess()) {
-                connectedEntry = entry;
-            } else {
-                wifiEntries.add(entry);
+        List<WifiEntry> wifiEntries = null;
+        final int accessPointsSize = (accessPoints == null) ? 0 : accessPoints.size();
+        final boolean hasMoreWifiEntries = (accessPointsSize > MAX_WIFI_ENTRY_COUNT);
+        if (accessPointsSize > 0) {
+            wifiEntries = new ArrayList<>();
+            final int count = hasMoreWifiEntries ? MAX_WIFI_ENTRY_COUNT : accessPointsSize;
+            mConnectedWifiInternetMonitor.unregisterCallback();
+            for (int i = 0; i < count; i++) {
+                WifiEntry entry = accessPoints.get(i);
+                mConnectedWifiInternetMonitor.registerCallbackIfNeed(entry);
+                if (connectedEntry == null && entry.isDefaultNetwork()
+                        && entry.hasInternetAccess()) {
+                    connectedEntry = entry;
+                } else {
+                    wifiEntries.add(entry);
+                }
             }
+            mHasWifiEntries = true;
+        } else {
+            mHasWifiEntries = false;
         }
-        mConnectedEntry = connectedEntry;
-        mWifiEntriesCount = wifiEntries.size();
 
-        if (mCallback != null) {
-            mCallback.onAccessPointsChanged(wifiEntries, mConnectedEntry);
-        }
+        mCallback.onAccessPointsChanged(wifiEntries, connectedEntry, hasMoreWifiEntries);
     }
 
     @Override
@@ -986,6 +978,55 @@
     }
 
     /**
+     * Helper class for monitoring the Internet access of the connected WifiEntry.
+     */
+    @VisibleForTesting
+    protected class ConnectedWifiInternetMonitor implements WifiEntry.WifiEntryCallback {
+
+        private WifiEntry mWifiEntry;
+
+        public void registerCallbackIfNeed(WifiEntry entry) {
+            if (entry == null || mWifiEntry != null) {
+                return;
+            }
+            // If the Wi-Fi is not connected yet, or it's the connected Wi-Fi with Internet
+            // access. Then we don't need to listen to the callback to update the Wi-Fi entries.
+            if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED
+                    || (entry.isDefaultNetwork() && entry.hasInternetAccess())) {
+                return;
+            }
+            mWifiEntry = entry;
+            entry.setListener(this);
+        }
+
+        public void unregisterCallback() {
+            if (mWifiEntry == null) {
+                return;
+            }
+            mWifiEntry.setListener(null);
+            mWifiEntry = null;
+        }
+
+        @MainThread
+        @Override
+        public void onUpdated() {
+            if (mWifiEntry == null) {
+                return;
+            }
+            WifiEntry entry = mWifiEntry;
+            if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED) {
+                unregisterCallback();
+                return;
+            }
+            if (entry.isDefaultNetwork() && entry.hasInternetAccess()) {
+                unregisterCallback();
+                // Trigger onAccessPointsChanged() to update the Wi-Fi entries.
+                scanWifiAccessPoints();
+            }
+        }
+    }
+
+    /**
      * Return {@code true} If the Ethernet exists
      */
     @MainThread
@@ -1060,7 +1101,7 @@
         void dismissDialog();
 
         void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
-                @Nullable WifiEntry connectedEntry);
+                @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries);
     }
 
     void makeOverlayToast(int stringId) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 3ed7e84..e7cd1e2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -76,6 +76,7 @@
 
 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.systemui.Dumpable;
@@ -88,6 +89,7 @@
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.navigationbar.buttons.KeyButtonView;
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.shared.recents.IOverviewProxy;
@@ -161,6 +163,7 @@
     private final Optional<StartingSurface> mStartingSurface;
     private final SmartspaceTransitionController mSmartspaceTransitionController;
     private final Optional<RecentTasks> mRecentTasks;
+    private final UiEventLogger mUiEventLogger;
 
     private Region mActiveNavBarRegion;
 
@@ -248,6 +251,7 @@
             mContext.getSystemService(InputMethodManager.class)
                     .showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
                             DEFAULT_DISPLAY);
+            mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
         }
 
         @Override
@@ -560,6 +564,7 @@
             ShellTransitions shellTransitions,
             ScreenLifecycle screenLifecycle,
             SmartspaceTransitionController smartspaceTransitionController,
+            UiEventLogger uiEventLogger,
             DumpManager dumpManager) {
         super(broadcastDispatcher);
         mContext = context;
@@ -581,6 +586,7 @@
         mOneHandedOptional = oneHandedOptional;
         mShellTransitions = shellTransitions;
         mRecentTasks = recentTasks;
+        mUiEventLogger = uiEventLogger;
 
         // Assumes device always starts with back button until launcher tells it that it does not
         mNavBarButtonAlpha = 1.0f;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 85bf98c..7f130cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents;
 
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
@@ -248,8 +249,8 @@
                     .setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
             View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
             WindowManagerWrapper wm = WindowManagerWrapper.getInstance();
-            if (!QuickStepContract.isGesturalMode(mNavBarMode) 
-            	    && wm.hasSoftNavigationBar(mContext.getDisplayId())) {
+            if (!QuickStepContract.isGesturalMode(mNavBarMode)
+            	    && wm.hasSoftNavigationBar(mContext.getDisplayId()) && !isTablet(mContext)) {
                 buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
                 swapChildrenIfRtlAndVertical(buttons);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
index c50365f..71c5fad 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
@@ -15,7 +15,8 @@
 class SensorUseDialog(
     context: Context,
     val sensor: Int,
-    val clickListener: DialogInterface.OnClickListener
+    val clickListener: DialogInterface.OnClickListener,
+    val dismissListener: DialogInterface.OnDismissListener
 ) : SystemUIDialog(context) {
 
     // TODO move to onCreate (b/200815309)
@@ -69,6 +70,8 @@
                 context.getString(com.android.internal.R.string
                         .cancel), clickListener)
 
+        setOnDismissListener(dismissListener)
+
         setCancelable(false)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index b0071d9..dae375a 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -50,7 +50,7 @@
     private val keyguardStateController: KeyguardStateController,
     private val keyguardDismissUtil: KeyguardDismissUtil,
     @Background private val bgHandler: Handler
-) : Activity(), DialogInterface.OnClickListener {
+) : Activity(), DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
 
     companion object {
         private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName
@@ -120,7 +120,7 @@
             }
         }
 
-        mDialog = SensorUseDialog(this, sensor, this)
+        mDialog = SensorUseDialog(this, sensor, this, this)
         mDialog!!.show()
     }
 
@@ -212,4 +212,8 @@
                     .suppressSensorPrivacyReminders(sensor, suppressed)
         }
     }
+
+    override fun onDismiss(dialog: DialogInterface?) {
+        finish()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index f43d9c3..d7b4738 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -887,7 +887,13 @@
                 mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
             }
         } else {
-            showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+            if (mKeyguardUpdateMonitor.isUdfpsSupported()
+                    && mKeyguardUpdateMonitor.getUserCanSkipBouncer(
+                    KeyguardUpdateMonitor.getCurrentUser())) {
+                showBiometricMessage(mContext.getString(R.string.keyguard_unlock_press));
+            } else {
+                showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java
index c4fadff..4551807 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java
@@ -40,7 +40,7 @@
             super(context);
             setTitle(R.string.user_remove_user_title);
             setMessage(context.getString(R.string.user_remove_user_message));
-            setButton(DialogInterface.BUTTON_NEGATIVE,
+            setButton(DialogInterface.BUTTON_NEUTRAL,
                     context.getString(android.R.string.cancel), this);
             setButton(DialogInterface.BUTTON_POSITIVE,
                     context.getString(R.string.user_remove_user_remove), this);
@@ -51,7 +51,7 @@
 
         @Override
         public void onClick(DialogInterface dialog, int which) {
-            if (which == BUTTON_NEGATIVE) {
+            if (which == BUTTON_NEUTRAL) {
                 cancel();
             } else {
                 dismiss();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
new file mode 100644
index 0000000..a1d086b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.charging
+
+import android.graphics.Color
+import android.graphics.PointF
+import android.graphics.RuntimeShader
+import android.util.MathUtils
+
+/**
+ * Shader class that renders a distorted ripple for the UDFPS dwell effect.
+ * Adjustable shader parameters:
+ *   - progress
+ *   - origin
+ *   - color
+ *   - time
+ *   - maxRadius
+ *   - distortionStrength.
+ * See per field documentation for more details.
+ *
+ * Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
+ */
+class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) {
+    companion object {
+        private const val SHADER_UNIFORMS = """uniform vec2 in_origin;
+                uniform float in_time;
+                uniform float in_radius;
+                uniform float in_blur;
+                uniform vec4 in_color;
+                uniform float in_phase1;
+                uniform float in_phase2;
+                uniform float in_distortion_strength;"""
+        private const val SHADER_LIB = """
+                float softCircle(vec2 uv, vec2 xy, float radius, float blur) {
+                  float blurHalf = blur * 0.5;
+                  float d = distance(uv, xy);
+                  return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);
+                }
+
+                float softRing(vec2 uv, vec2 xy, float radius, float blur) {
+                  float thickness_half = radius * 0.25;
+                  float circle_outer = softCircle(uv, xy, radius + thickness_half, blur);
+                  float circle_inner = softCircle(uv, xy, radius - thickness_half, blur);
+                  return circle_outer - circle_inner;
+                }
+
+                vec2 distort(vec2 p, float time, float distort_amount_xy, float frequency) {
+                    return p + vec2(sin(p.x * frequency + in_phase1),
+                                    cos(p.y * frequency * 1.23 + in_phase2)) * distort_amount_xy;
+                }
+
+                vec4 ripple(vec2 p, float distort_xy, float frequency) {
+                    vec2 p_distorted = distort(p, in_time, distort_xy, frequency);
+                    float circle = softCircle(p_distorted, in_origin, in_radius * 1.2, in_blur);
+                    float rippleAlpha = max(circle,
+                        softRing(p_distorted, in_origin, in_radius, in_blur)) * 0.25;
+                    return in_color * rippleAlpha;
+                }
+                """
+        private const val SHADER_MAIN = """vec4 main(vec2 p) {
+                    vec4 color1 = ripple(p,
+                        12 * in_distortion_strength, // distort_xy
+                        0.012 // frequency
+                    );
+                    vec4 color2 = ripple(p,
+                        17.5 * in_distortion_strength, // distort_xy
+                        0.018 // frequency
+                    );
+                    // Alpha blend between two layers.
+                    return vec4(color1.xyz + color2.xyz
+                        * (1 - color1.w), color1.w + color2.w * (1-color1.w));
+                }"""
+        private const val SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN
+    }
+
+    /**
+     * Maximum radius of the ripple.
+     */
+    var maxRadius: Float = 0.0f
+
+    /**
+     * Origin coordinate of the ripple.
+     */
+    var origin: PointF = PointF()
+        set(value) {
+            field = value
+            setUniform("in_origin", floatArrayOf(value.x, value.y))
+        }
+
+    /**
+     * Progress of the ripple. Float value between [0, 1].
+     */
+    var progress: Float = 0.0f
+        set(value) {
+            field = value
+            setUniform("in_radius",
+                    (1 - (1 - value) * (1 - value) * (1 - value))* maxRadius)
+            setUniform("in_blur", MathUtils.lerp(1f, 0.7f, value))
+        }
+
+    /**
+     * Distortion strength between [0, 1], with 0 being no distortion and 1 being full distortion.
+     */
+    var distortionStrength: Float = 0.0f
+        set(value) {
+            field = value
+            setUniform("in_distortion_strength", value)
+        }
+
+    /**
+     * Play time since the start of the effect in seconds.
+     */
+    var time: Float = 0.0f
+        set(value) {
+            field = value * 0.001f
+            setUniform("in_time", field)
+            setUniform("in_phase1", field * 2f + 0.367f)
+            setUniform("in_phase2", field * 5.2f * 1.531f)
+        }
+
+    /**
+     * A hex value representing the ripple color, in the format of ARGB
+     */
+    var color: Int = 0xffffff.toInt()
+        set(value) {
+            field = value
+            val color = Color.valueOf(value)
+            setUniform("in_color", floatArrayOf(color.red(),
+                    color.green(), color.blue(), color.alpha()))
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index 04c60fc..8909e9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -93,9 +93,8 @@
                 nowPluggedIn: Boolean,
                 charging: Boolean
             ) {
-                // Suppresses the ripple when it's disabled, or when the state change comes
-                // from wireless charging.
-                if (!rippleEnabled || batteryController.isPluggedInWireless) {
+                // Suppresses the ripple when the state change comes from wireless charging.
+                if (batteryController.isPluggedInWireless) {
                     return
                 }
                 val wasPluggedIn = pluggedIn
@@ -145,7 +144,7 @@
     }
 
     fun startRipple() {
-        if (!rippleEnabled || rippleView.rippleInProgress || rippleView.parent != null) {
+        if (rippleView.rippleInProgress || rippleView.parent != null) {
             // Skip if ripple is still playing, or not playing but already added the parent
             // (which might happen just before the animation starts or right after
             // the animation ends.)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 2eb2065..63cb4ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -579,14 +579,24 @@
         if (contentView.hasOverlappingRendering()) {
             int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE
                     : LAYER_TYPE_HARDWARE;
-            int currentLayerType = contentView.getLayerType();
-            if (currentLayerType != layerType) {
-                contentView.setLayerType(layerType, null);
-            }
+            contentView.setLayerType(layerType, null);
         }
         contentView.setAlpha(contentAlpha);
+        // After updating the current view, reset all views.
+        if (contentAlpha == 1f) {
+            resetAllContentAlphas();
+        }
     }
 
+    /**
+     * If a subclass's {@link #getContentView()} returns different views depending on state,
+     * this method is an opportunity to reset the alpha of ALL content views, not just the
+     * current one, which may prevent a content view that is temporarily hidden from being reset.
+     *
+     * This should setAlpha(1.0f) and setLayerType(LAYER_TYPE_NONE) for all content views.
+     */
+    protected void resetAllContentAlphas() {}
+
     @Override
     protected void applyRoundness() {
         super.applyRoundness();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 23a0a75..819f5a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2130,15 +2130,6 @@
     }
 
     public void setExpandAnimationRunning(boolean expandAnimationRunning) {
-        View contentView;
-        if (mIsSummaryWithChildren) {
-            contentView =  mChildrenContainer;
-        } else {
-            contentView = getShowingLayout();
-        }
-        if (mGuts != null && mGuts.isExposed()) {
-            contentView = mGuts;
-        }
         if (expandAnimationRunning) {
             setAboveShelf(true);
             mExpandAnimationRunning = true;
@@ -2151,9 +2142,7 @@
             if (mGuts != null) {
                 mGuts.setAlpha(1.0f);
             }
-            if (contentView != null) {
-                contentView.setAlpha(1.0f);
-            }
+            resetAllContentAlphas();
             setExtraWidthForClipping(0.0f);
             if (mNotificationParent != null) {
                 mNotificationParent.setExtraWidthForClipping(0.0f);
@@ -2601,10 +2590,8 @@
             mPrivateLayout.animate().cancel();
             if (mChildrenContainer != null) {
                 mChildrenContainer.animate().cancel();
-                mChildrenContainer.setAlpha(1f);
             }
-            mPublicLayout.setAlpha(1f);
-            mPrivateLayout.setAlpha(1f);
+            resetAllContentAlphas();
             mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
             updateChildrenVisibility();
         } else {
@@ -2631,7 +2618,10 @@
                     .alpha(0f)
                     .setStartDelay(delay)
                     .setDuration(duration)
-                    .withEndAction(() -> hiddenView.setVisibility(View.INVISIBLE));
+                    .withEndAction(() -> {
+                        hiddenView.setVisibility(View.INVISIBLE);
+                        resetAllContentAlphas();
+                    });
         }
         for (View showView : shownChildren) {
             showView.setVisibility(View.VISIBLE);
@@ -2754,12 +2744,7 @@
         if (wasAppearing) {
             // During the animation the visible view might have changed, so let's make sure all
             // alphas are reset
-            if (mChildrenContainer != null) {
-                mChildrenContainer.setAlpha(1.0f);
-            }
-            for (NotificationContentView l : mLayouts) {
-                l.setAlpha(1.0f);
-            }
+            resetAllContentAlphas();
             if (FADE_LAYER_OPTIMIZATION_ENABLED) {
                 setNotificationFaded(false);
             } else {
@@ -2770,6 +2755,18 @@
         }
     }
 
+    @Override
+    protected void resetAllContentAlphas() {
+        mPrivateLayout.setAlpha(1f);
+        mPrivateLayout.setLayerType(LAYER_TYPE_NONE, null);
+        mPublicLayout.setAlpha(1f);
+        mPublicLayout.setLayerType(LAYER_TYPE_NONE, null);
+        if (mChildrenContainer != null) {
+            mChildrenContainer.setAlpha(1f);
+            mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
+        }
+    }
+
     /** Gets the last value set with {@link #setNotificationFaded(boolean)} */
     @Override
     public boolean isNotificationFaded() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index a9cc3237..e658468 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -59,6 +59,7 @@
     private float mMaxHeadsUpTranslation;
     private boolean mDismissAllInProgress;
     private int mLayoutMinHeight;
+    private int mLayoutMaxHeight;
     private NotificationShelf mShelf;
     private int mZDistanceBetweenElements;
     private int mBaseZHeight;
@@ -326,6 +327,14 @@
         mLayoutHeight = layoutHeight;
     }
 
+    public void setLayoutMaxHeight(int maxLayoutHeight) {
+        mLayoutMaxHeight = maxLayoutHeight;
+    }
+
+    public int getLayoutMaxHeight() {
+        return mLayoutMaxHeight;
+    }
+
     public float getTopPadding() {
         return mTopPadding;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 5477c19..7923179 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -668,6 +668,7 @@
 
     public void setIsRemoteInputActive(boolean isActive) {
         mIsRemoteInputActive = isActive;
+        updateFooter();
     }
 
     @VisibleForTesting
@@ -682,6 +683,7 @@
         boolean showFooterView = (showDismissView || getVisibleNotificationCount() > 0)
                 && mIsCurrentUserSetup  // see: b/193149550
                 && mStatusBarState != StatusBarState.KEYGUARD
+                && mQsExpansionFraction != 1
                 && !mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
                 && !mIsRemoteInputActive;
         boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(),
@@ -1109,6 +1111,7 @@
     @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
     private void updateAlgorithmHeightAndPadding() {
         mAmbientState.setLayoutHeight(getLayoutHeight());
+        mAmbientState.setLayoutMaxHeight(mMaxLayoutHeight);
         updateAlgorithmLayoutMinHeight();
         mAmbientState.setTopPadding(mTopPadding);
     }
@@ -3984,6 +3987,7 @@
             updateChronometers();
             requestChildrenUpdate();
             updateUseRoundedRectClipping();
+            updateDismissBehavior();
         }
     }
 
@@ -4766,6 +4770,8 @@
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setQsExpansionFraction(float qsExpansionFraction) {
+        boolean footerAffected = mQsExpansionFraction != qsExpansionFraction
+                && (mQsExpansionFraction == 1 || qsExpansionFraction == 1);
         mQsExpansionFraction = qsExpansionFraction;
         updateUseRoundedRectClipping();
 
@@ -4774,6 +4780,9 @@
         if (mOwnScrollY > 0) {
             setOwnScrollY((int) MathUtils.lerp(mOwnScrollY, 0, mQsExpansionFraction));
         }
+        if (footerAffected) {
+            updateFooter();
+        }
     }
 
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
@@ -4925,6 +4934,10 @@
         StringBuilder sb = new StringBuilder("[")
                 .append(this.getClass().getSimpleName()).append(":")
                 .append(" pulsing=").append(mPulsing ? "T" : "f")
+                .append(" expanded=").append(mIsExpanded ? "T" : "f")
+                .append(" headsUpPinned=").append(mInHeadsUpPinnedMode ? "T" : "f")
+                .append(" qsClipping=").append(mShouldUseRoundedRectClipping ? "T" : "f")
+                .append(" qsClipDismiss=").append(mDismissUsingRowTranslationX ? "T" : "f")
                 .append(" visibility=").append(DumpUtilsKt.visibilityString(getVisibility()))
                 .append(" alpha=").append(getAlpha())
                 .append(" scrollY=").append(mAmbientState.getScrollY())
@@ -5452,7 +5465,7 @@
         // On the split keyguard, dismissing with clipping without a visual boundary looks odd,
         // so let's use the content dismiss behavior instead.
         boolean dismissUsingRowTranslationX = !mShouldUseSplitNotificationShade
-                || mStatusBarState != StatusBarState.KEYGUARD;
+                || (mStatusBarState != StatusBarState.KEYGUARD && mIsExpanded);
         if (mDismissUsingRowTranslationX != dismissUsingRowTranslationX) {
             mDismissUsingRowTranslationX = dismissUsingRowTranslationX;
             for (int i = 0; i < getChildCount(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 015edb8..2c70a5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -29,6 +29,7 @@
 import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.R;
 import com.android.systemui.animation.ShadeInterpolation;
+import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -60,6 +61,7 @@
     @VisibleForTesting float mHeadsUpInset;
     private int mPinnedZTranslationExtra;
     private float mNotificationScrimPadding;
+    private int mCloseHandleUnderlapHeight;
 
     public StackScrollAlgorithm(
             Context context,
@@ -85,6 +87,7 @@
                 R.dimen.heads_up_pinned_elevation);
         mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
         mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
+        mCloseHandleUnderlapHeight = res.getDimensionPixelSize(R.dimen.close_handle_underlap);
     }
 
     /**
@@ -459,13 +462,17 @@
                                 && !hasOngoingNotifs(algorithmState));
             }
         } else {
-            if (view != ambientState.getTrackedHeadsUpRow()) {
+            if (view instanceof EmptyShadeView) {
+                float fullHeight = ambientState.getLayoutMaxHeight() + mCloseHandleUnderlapHeight
+                        - ambientState.getStackY();
+                viewState.yTranslation = (fullHeight - getMaxAllowedChildHeight(view)) / 2f;
+            } else if (view != ambientState.getTrackedHeadsUpRow()) {
                 if (ambientState.isExpansionChanging()) {
                     // We later update shelf state, then hide views below the shelf.
                     viewState.hidden = false;
                     viewState.inShelf = algorithmState.firstViewInShelf != null
                             && i >= algorithmState.visibleChildren.indexOf(
-                                    algorithmState.firstViewInShelf);
+                            algorithmState.firstViewInShelf);
                 } else if (ambientState.getShelf() != null) {
                     // When pulsing (incoming notification on AOD), innerHeight is 0; clamp all
                     // to shelf start, thereby hiding all notifications (except the first one, which
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index aa3b3e1..ad1c232 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -74,7 +74,7 @@
     private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
     private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
     private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
-    private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 3;
+    private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 2;
 
     @IntDef(prefix = { "MODE_" }, value = {
             MODE_NONE,
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 261b5db..5eb35ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -3262,12 +3262,6 @@
                     mStatusBarStateController.setState(KEYGUARD);
                 }
                 return true;
-            case StatusBarState.SHADE:
-
-                // This gets called in the middle of the touch handling, where the state is still
-                // that we are tracking the panel. Collapse the panel after this is done.
-                mView.post(mPostCollapseRunnable);
-                return false;
             default:
                 return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 2823d98..2bf16fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -928,7 +928,6 @@
 
     private void abortAnimations() {
         cancelHeightAnimator();
-        mView.removeCallbacks(mPostCollapseRunnable);
         mView.removeCallbacks(mFlingCollapseRunnable);
     }
 
@@ -1105,13 +1104,6 @@
         return onMiddleClicked();
     }
 
-    protected final Runnable mPostCollapseRunnable = new Runnable() {
-        @Override
-        public void run() {
-            collapse(false /* delayed */, 1.0f /* speedUpFactor */);
-        }
-    };
-
     protected abstract boolean onMiddleClicked();
 
     protected abstract boolean isDozing();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index bceffb3..401c1b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -46,10 +46,11 @@
         private val SPLIT_HEADER_TRANSITION_ID = R.id.split_header_transition
     }
 
+    private val carrierIconSlots: List<String>
     private val combinedHeaders = featureFlags.useCombinedQSHeaders()
-    // TODO(b/194178072) Handle RSSI hiding when multi carrier
     private val iconManager: StatusBarIconController.IconManager
     private val qsCarrierGroupController: QSCarrierGroupController
+    private val iconContainer: StatusIconContainer
     private var visible = false
         set(value) {
             if (field == value) {
@@ -115,7 +116,16 @@
         batteryMeterViewController.ignoreTunerUpdates()
         batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
 
-        val iconContainer: StatusIconContainer = statusBar.findViewById(R.id.statusIcons)
+        carrierIconSlots = if (featureFlags.isCombinedStatusBarSignalIconsEnabled) {
+            listOf(
+                statusBar.context.getString(com.android.internal.R.string.status_bar_no_calling),
+                statusBar.context.getString(com.android.internal.R.string.status_bar_call_strength)
+            )
+        } else {
+            listOf(statusBar.context.getString(com.android.internal.R.string.status_bar_mobile))
+        }
+
+        iconContainer = statusBar.findViewById(R.id.statusIcons)
         iconManager = StatusBarIconController.IconManager(iconContainer, featureFlags)
         qsCarrierGroupController = qsCarrierGroupControllerBuilder
                 .setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
@@ -181,9 +191,20 @@
     private fun updateListeners() {
         qsCarrierGroupController.setListening(visible)
         if (visible) {
+            updateSingleCarrier(qsCarrierGroupController.isSingleCarrier)
+            qsCarrierGroupController.setOnSingleCarrierChangedListener { updateSingleCarrier(it) }
             statusBarIconController.addIconGroup(iconManager)
         } else {
+            qsCarrierGroupController.setOnSingleCarrierChangedListener(null)
             statusBarIconController.removeIconGroup(iconManager)
         }
     }
+
+    private fun updateSingleCarrier(singleCarrier: Boolean) {
+        if (singleCarrier) {
+            iconContainer.removeIgnoredSlots(carrierIconSlots)
+        } else {
+            iconContainer.addIgnoredSlots(carrierIconSlots)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index b84e6e6..e788928 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -416,7 +416,7 @@
      * dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
      */
     public void showGenericBouncer(boolean scrimmed) {
-        if (mAlternateAuthInterceptor != null) {
+        if (shouldShowAltAuth()) {
             updateAlternateAuthShowing(mAlternateAuthInterceptor.showAlternateAuthBouncer());
             return;
         }
@@ -424,6 +424,11 @@
         showBouncer(scrimmed);
     }
 
+    private boolean shouldShowAltAuth() {
+        return mAlternateAuthInterceptor != null
+                && mKeyguardUpdateManager.isUnlockingWithBiometricAllowed(true);
+    }
+
     /**
      * Hides the input bouncer (pin/password/pattern).
      */
@@ -479,7 +484,7 @@
 
             // If there is an an alternate auth interceptor (like the UDFPS), show that one instead
             // of the bouncer.
-            if (mAlternateAuthInterceptor != null) {
+            if (shouldShowAltAuth()) {
                 if (!afterKeyguardGone) {
                     mBouncer.setDismissAction(mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
                     mAfterKeyguardGoneAction = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 43264b6..93f9892 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -45,6 +45,10 @@
 
 /**
  * Base class for dialogs that should appear over panels and keyguard.
+ *
+ * Optionally provide a {@link SystemUIDialogManager} to its constructor to send signals to
+ * listeners on whether this dialog is showing.
+ *
  * The SystemUIDialog registers a listener for the screen off / close system dialogs broadcast,
  * and dismisses itself when it receives the broadcast.
  */
@@ -54,8 +58,9 @@
             "persist.systemui.flag_tablet_dialog_width";
 
     private final Context mContext;
-    private final DismissReceiver mDismissReceiver;
+    @Nullable private final DismissReceiver mDismissReceiver;
     private final Handler mHandler = new Handler();
+    @Nullable private final SystemUIDialogManager mDialogManager;
 
     private int mLastWidth = Integer.MIN_VALUE;
     private int mLastHeight = Integer.MIN_VALUE;
@@ -66,11 +71,22 @@
         this(context, R.style.Theme_SystemUI_Dialog);
     }
 
+    public SystemUIDialog(Context context, SystemUIDialogManager dialogManager) {
+        this(context, R.style.Theme_SystemUI_Dialog, true, dialogManager);
+    }
+
     public SystemUIDialog(Context context, int theme) {
         this(context, theme, true /* dismissOnDeviceLock */);
     }
-
     public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock) {
+        this(context, theme, dismissOnDeviceLock, null);
+    }
+
+    /**
+     * @param udfpsDialogManager If set, UDFPS will hide if this dialog is showing.
+     */
+    public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock,
+            SystemUIDialogManager dialogManager) {
         super(context, theme);
         mContext = context;
 
@@ -80,6 +96,7 @@
         getWindow().setAttributes(attrs);
 
         mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this) : null;
+        mDialogManager = dialogManager;
     }
 
     @Override
@@ -126,30 +143,7 @@
      * the device configuration changes, and the result will be used to resize this dialog window.
      */
     protected int getWidth() {
-        boolean isOnTablet =
-                mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
-        if (!isOnTablet) {
-            return ViewGroup.LayoutParams.MATCH_PARENT;
-        }
-
-        int flagValue = SystemProperties.getInt(FLAG_TABLET_DIALOG_WIDTH, 0);
-        if (flagValue == -1) {
-            // The width of bottom sheets (624dp).
-            return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 624,
-                    mContext.getResources().getDisplayMetrics()));
-        } else if (flagValue == -2) {
-            // The suggested small width for all dialogs (348dp)
-            return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 348,
-                    mContext.getResources().getDisplayMetrics()));
-        } else if (flagValue > 0) {
-            // Any given width.
-            return Math.round(
-                    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, flagValue,
-                            mContext.getResources().getDisplayMetrics()));
-        } else {
-            // By default we use the same width as the notification shade in portrait mode (504dp).
-            return mContext.getResources().getDimensionPixelSize(R.dimen.large_dialog_width);
-        }
+        return getDefaultDialogWidth(mContext);
     }
 
     /**
@@ -157,7 +151,7 @@
      * the device configuration changes, and the result will be used to resize this dialog window.
      */
     protected int getHeight() {
-        return ViewGroup.LayoutParams.WRAP_CONTENT;
+        return getDefaultDialogHeight();
     }
 
     @Override
@@ -168,6 +162,10 @@
             mDismissReceiver.register();
         }
 
+        if (mDialogManager != null) {
+            mDialogManager.setShowing(this, true);
+        }
+
         // Listen for configuration changes to resize this dialog window. This is mostly necessary
         // for foldables that often go from large <=> small screen when folding/unfolding.
         ViewRootImpl.addConfigCallback(this);
@@ -181,6 +179,10 @@
             mDismissReceiver.unregister();
         }
 
+        if (mDialogManager != null) {
+            mDialogManager.setShowing(this, false);
+        }
+
         ViewRootImpl.removeConfigCallback(this);
     }
 
@@ -267,6 +269,45 @@
         dismissReceiver.register();
     }
 
+    /** Set an appropriate size to {@code dialog} depending on the current configuration. */
+    public static void setDialogSize(Dialog dialog) {
+        // We need to create the dialog first, otherwise the size will be overridden when it is
+        // created.
+        dialog.create();
+        dialog.getWindow().setLayout(getDefaultDialogWidth(dialog.getContext()),
+                getDefaultDialogHeight());
+    }
+
+    private static int getDefaultDialogWidth(Context context) {
+        boolean isOnTablet = context.getResources().getConfiguration().smallestScreenWidthDp >= 600;
+        if (!isOnTablet) {
+            return ViewGroup.LayoutParams.MATCH_PARENT;
+        }
+
+        int flagValue = SystemProperties.getInt(FLAG_TABLET_DIALOG_WIDTH, 0);
+        if (flagValue == -1) {
+            // The width of bottom sheets (624dp).
+            return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 624,
+                    context.getResources().getDisplayMetrics()));
+        } else if (flagValue == -2) {
+            // The suggested small width for all dialogs (348dp)
+            return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 348,
+                    context.getResources().getDisplayMetrics()));
+        } else if (flagValue > 0) {
+            // Any given width.
+            return Math.round(
+                    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, flagValue,
+                            context.getResources().getDisplayMetrics()));
+        } else {
+            // By default we use the same width as the notification shade in portrait mode (504dp).
+            return context.getResources().getDimensionPixelSize(R.dimen.large_dialog_width);
+        }
+    }
+
+    private static int getDefaultDialogHeight() {
+        return ViewGroup.LayoutParams.WRAP_CONTENT;
+    }
+
     private static class DismissReceiver extends BroadcastReceiver {
         private static final IntentFilter INTENT_FILTER = new IntentFilter();
         static {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java
new file mode 100644
index 0000000..204f710
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+/**
+ * Register dialogs to this manager if extraneous affordances (like the UDFPS sensor area)
+ * should be hidden from the screen when the dialog shows.
+ *
+ * Currently, only used if UDFPS is supported on the device; however, can be extended in the future
+ * for other use cases.
+ */
+@SysUISingleton
+public class SystemUIDialogManager implements Dumpable {
+    private final StatusBarKeyguardViewManager mKeyguardViewManager;
+
+    private final Set<SystemUIDialog> mDialogsShowing = new HashSet<>();
+    private final Set<Listener> mListeners = new HashSet<>();
+
+    @Inject
+    public SystemUIDialogManager(
+            DumpManager dumpManager,
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+        dumpManager.registerDumpable(this);
+        mKeyguardViewManager = statusBarKeyguardViewManager;
+    }
+
+    /**
+     * Whether listeners should hide affordances like the UDFPS sensor icon.
+     */
+    public boolean shouldHideAffordance() {
+        return !mDialogsShowing.isEmpty();
+    }
+
+    /**
+     * Register a listener to receive callbacks.
+     */
+    public void registerListener(@NonNull Listener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Unregister a listener from receiving callbacks.
+     */
+    public void unregisterListener(@NonNull Listener listener) {
+        mListeners.remove(listener);
+    }
+
+    void setShowing(SystemUIDialog dialog, boolean showing) {
+        final boolean wasHidingAffordances = shouldHideAffordance();
+        if (showing) {
+            mDialogsShowing.add(dialog);
+        } else {
+            mDialogsShowing.remove(dialog);
+        }
+
+        if (wasHidingAffordances != shouldHideAffordance()) {
+            updateDialogListeners();
+        }
+    }
+
+    private void updateDialogListeners() {
+        if (shouldHideAffordance()) {
+            mKeyguardViewManager.resetAlternateAuth(true);
+        }
+
+        for (Listener listener : mListeners) {
+            listener.shouldHideAffordances(shouldHideAffordance());
+        }
+    }
+
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println("listeners:");
+        for (Listener listener : mListeners) {
+            pw.println("\t" + listener);
+        }
+        pw.println("dialogs tracked:");
+        for (SystemUIDialog dialog : mDialogsShowing) {
+            pw.println("\t" + dialog);
+        }
+    }
+
+    /** SystemUIDialogManagerListener */
+    public interface Listener {
+        /**
+         * Callback where shouldHide=true if listeners should hide their views that may overlap
+         * a showing dialog.
+         */
+        void shouldHideAffordances(boolean shouldHide);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 36e56f9..ebf5a6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -1161,7 +1161,7 @@
                     ? com.android.settingslib.R.string.guest_reset_guest_dialog_title
                     : R.string.guest_exit_guest_dialog_title);
             setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
-            setButton(DialogInterface.BUTTON_NEGATIVE,
+            setButton(DialogInterface.BUTTON_NEUTRAL,
                     context.getString(android.R.string.cancel), this);
             setButton(DialogInterface.BUTTON_POSITIVE,
                     context.getString(mGuestUserAutoCreated
@@ -1180,7 +1180,7 @@
             if (mFalsingManager.isFalseTap(penalty)) {
                 return;
             }
-            if (which == BUTTON_NEGATIVE) {
+            if (which == BUTTON_NEUTRAL) {
                 cancel();
             } else {
                 mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
@@ -1198,7 +1198,7 @@
             super(context);
             setTitle(R.string.user_add_user_title);
             setMessage(context.getString(R.string.user_add_user_message_short));
-            setButton(DialogInterface.BUTTON_NEGATIVE,
+            setButton(DialogInterface.BUTTON_NEUTRAL,
                     context.getString(android.R.string.cancel), this);
             setButton(DialogInterface.BUTTON_POSITIVE,
                     context.getString(android.R.string.ok), this);
@@ -1212,7 +1212,7 @@
             if (mFalsingManager.isFalseTap(penalty)) {
                 return;
             }
-            if (which == BUTTON_NEGATIVE) {
+            if (which == BUTTON_NEUTRAL) {
                 cancel();
             } else {
                 mDialogLaunchAnimator.dismissStack(this);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index b546edf..1fd47a4 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -56,6 +56,7 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tracing.nano.SystemUiTraceProto;
 import com.android.wm.shell.ShellCommandHandler;
+import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.draganddrop.DragAndDrop;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -66,7 +67,6 @@
 import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.protolog.ShellProtoLogImpl;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
 import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.io.FileDescriptor;
@@ -114,7 +114,7 @@
     private final Optional<OneHanded> mOneHandedOptional;
     private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
     private final Optional<ShellCommandHandler> mShellCommandHandler;
-    private final Optional<SizeCompatUI> mSizeCompatUIOptional;
+    private final Optional<CompatUI> mCompatUIOptional;
     private final Optional<DragAndDrop> mDragAndDropOptional;
 
     private final CommandQueue mCommandQueue;
@@ -132,7 +132,7 @@
     private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
     private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
     private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
-    private KeyguardUpdateMonitorCallback mSizeCompatUIKeyguardCallback;
+    private KeyguardUpdateMonitorCallback mCompatUIKeyguardCallback;
     private WakefulnessLifecycle.Observer mWakefulnessObserver;
 
     @Inject
@@ -143,7 +143,7 @@
             Optional<OneHanded> oneHandedOptional,
             Optional<HideDisplayCutout> hideDisplayCutoutOptional,
             Optional<ShellCommandHandler> shellCommandHandler,
-            Optional<SizeCompatUI> sizeCompatUIOptional,
+            Optional<CompatUI> sizeCompatUIOptional,
             Optional<DragAndDrop> dragAndDropOptional,
             CommandQueue commandQueue,
             ConfigurationController configurationController,
@@ -169,7 +169,7 @@
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mProtoTracer = protoTracer;
         mShellCommandHandler = shellCommandHandler;
-        mSizeCompatUIOptional = sizeCompatUIOptional;
+        mCompatUIOptional = sizeCompatUIOptional;
         mDragAndDropOptional = dragAndDropOptional;
         mSysUiMainExecutor = sysUiMainExecutor;
     }
@@ -185,7 +185,7 @@
         mSplitScreenOptional.ifPresent(this::initSplitScreen);
         mOneHandedOptional.ifPresent(this::initOneHanded);
         mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
-        mSizeCompatUIOptional.ifPresent(this::initSizeCompatUi);
+        mCompatUIOptional.ifPresent(this::initCompatUi);
         mDragAndDropOptional.ifPresent(this::initDragAndDrop);
     }
 
@@ -391,14 +391,14 @@
     }
 
     @VisibleForTesting
-    void initSizeCompatUi(SizeCompatUI sizeCompatUI) {
-        mSizeCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+    void initCompatUi(CompatUI sizeCompatUI) {
+        mCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() {
             @Override
             public void onKeyguardOccludedChanged(boolean occluded) {
                 sizeCompatUI.onKeyguardOccludedChanged(occluded);
             }
         };
-        mKeyguardUpdateMonitor.registerCallback(mSizeCompatUIKeyguardCallback);
+        mKeyguardUpdateMonitor.registerCallback(mCompatUIKeyguardCallback);
     }
 
     void initDragAndDrop(DragAndDrop dragAndDrop) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index ef9b850..70792cf 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -669,7 +669,7 @@
         mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
                 .onAuthenticationError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
 
-        verify(mLockPatternUtils, never()).requireStrongAuth(anyInt(), anyInt());
+        verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
     }
 
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 9e42ff3..ac221a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -65,6 +65,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -170,6 +171,8 @@
     private TypedArray mBrightnessValues;
     @Mock
     private TypedArray mBrightnessBacklight;
+    @Mock
+    private SystemUIDialogManager mSystemUIDialogManager;
 
     // Capture listeners so that they can be used to send events
     @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
@@ -179,8 +182,6 @@
     @Captor private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor;
     private ScreenLifecycle.Observer mScreenObserver;
 
-    @Captor private ArgumentCaptor<UdfpsAnimationViewController> mAnimViewControllerCaptor;
-
     @Before
     public void setUp() {
         setUpResources();
@@ -238,7 +239,8 @@
                 mHandler,
                 mConfigurationController,
                 mSystemClock,
-                mUnlockedScreenOffAnimationController);
+                mUnlockedScreenOffAnimationController,
+                mSystemUIDialogManager);
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
         verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -642,12 +644,12 @@
         mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
         moveEvent.recycle();
 
-        // THEN low-tick haptic is played
+        // THEN click haptic is played
         verify(mVibrator).vibrate(
                 anyInt(),
                 anyString(),
                 any(),
-                eq("udfps-onStart-tick"),
+                eq("udfps-onStart-click"),
                 eq(UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES));
 
         // THEN make sure vibration attributes has so that it always will play the haptic,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 1cf21ac..0ae3c39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -92,6 +93,8 @@
     @Mock
     private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     @Mock
+    private SystemUIDialogManager mDialogManager;
+    @Mock
     private UdfpsController mUdfpsController;
     private FakeSystemClock mSystemClock = new FakeSystemClock();
 
@@ -130,6 +133,7 @@
                 mSystemClock,
                 mKeyguardStateController,
                 mUnlockedScreenOffAnimationController,
+                mDialogManager,
                 mUdfpsController);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
new file mode 100644
index 0000000..87b9172
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.app.PendingIntent
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.wm.shell.TaskView
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class DetailDialogTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var taskView: TaskView
+    @Mock
+    private lateinit var controlViewHolder: ControlViewHolder
+    @Mock
+    private lateinit var pendingIntent: PendingIntent
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @Test
+    fun testPendingIntentIsUnModified() {
+        // GIVEN the dialog is created with a PendingIntent
+        val dialog = createDialog(pendingIntent)
+
+        // WHEN the TaskView is initialized
+        dialog.stateCallback.onInitialized()
+
+        // THEN the PendingIntent used to call startActivity is unmodified by systemui
+        verify(taskView).startActivity(eq(pendingIntent), any(), any(), any())
+    }
+
+    private fun createDialog(pendingIntent: PendingIntent): DetailDialog {
+        return DetailDialog(
+            mContext,
+            taskView,
+            pendingIntent,
+            controlViewHolder
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index bf5522c..e3a7e3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -62,6 +62,7 @@
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.telephony.TelephonyListenerManager;
@@ -117,6 +118,7 @@
     @Mock private StatusBar mStatusBar;
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock private DialogLaunchAnimator mDialogLaunchAnimator;
+    @Mock private SystemUIDialogManager mDialogManager;
 
     private TestableLooper mTestableLooper;
 
@@ -162,7 +164,8 @@
                 mPackageManager,
                 Optional.of(mStatusBar),
                 mKeyguardUpdateMonitor,
-                mDialogLaunchAnimator);
+                mDialogLaunchAnimator,
+                mDialogManager);
         mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
 
         ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 81bcbfb..d7c00fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -26,7 +26,6 @@
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -43,7 +42,6 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Pair;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
@@ -69,8 +67,6 @@
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
-import com.airbnb.lottie.LottieAnimationView;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -110,8 +106,6 @@
     private @Mock ConfigurationController mConfigurationController;
     private @Mock Vibrator mVibrator;
     private @Mock AuthRippleController mAuthRippleController;
-    private @Mock LottieAnimationView mAodFp;
-    private @Mock LayoutInflater mLayoutInflater;
     private FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
 
     private LockIconViewController mLockIconViewController;
@@ -149,7 +143,6 @@
 
         when(mLockIconView.getResources()).thenReturn(mResources);
         when(mLockIconView.getContext()).thenReturn(mContext);
-        when(mLockIconView.findViewById(R.layout.udfps_aod_lock_icon)).thenReturn(mAodFp);
         when(mContext.getResources()).thenReturn(mResources);
         when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager);
         Rect windowBounds = new Rect(0, 0, 800, 1200);
@@ -176,8 +169,7 @@
                 mDelayableExecutor,
                 mVibrator,
                 mAuthRippleController,
-                mResources,
-                mLayoutInflater
+                mResources
         );
     }
 
@@ -187,35 +179,6 @@
     }
 
     @Test
-    public void testIgnoreUdfpsWhenNotSupported() {
-        // GIVEN Udpfs sensor is NOT available
-        mLockIconViewController.init();
-        captureAttachListener();
-
-        // WHEN the view is attached
-        mAttachListener.onViewAttachedToWindow(mLockIconView);
-
-        // THEN lottie animation should NOT be inflated
-        verify(mLayoutInflater, never()).inflate(eq(R.layout.udfps_aod_lock_icon), any());
-    }
-
-    @Test
-    public void testInflateUdfpsWhenSupported() {
-        // GIVEN Udpfs sensor is available
-        setupUdfps();
-        when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
-
-        mLockIconViewController.init();
-        captureAttachListener();
-
-        // WHEN the view is attached
-        mAttachListener.onViewAttachedToWindow(mLockIconView);
-
-        // THEN lottie animation should be inflated
-        verify(mLayoutInflater).inflate(eq(R.layout.udfps_aod_lock_icon), any());
-    }
-
-    @Test
     public void testUpdateFingerprintLocationOnInit() {
         // GIVEN fp sensor location is available pre-attached
         Pair<Integer, PointF> udfps = setupUdfps();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 053851e..451291d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -43,6 +43,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -65,6 +66,7 @@
             mock(NotificationEntryManager.class);
     private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
     private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+    private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
 
     private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
     private MediaOutputController mMediaOutputController;
@@ -77,7 +79,7 @@
     public void setUp() {
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
         mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
                 mMediaOutputController);
         mMediaOutputBaseDialogImpl.onCreate(new Bundle());
@@ -167,7 +169,7 @@
     class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog {
 
         MediaOutputBaseDialogImpl(Context context, MediaOutputController mediaOutputController) {
-            super(context, mediaOutputController);
+            super(context, mediaOutputController, mDialogManager);
 
             mAdapter = mMediaOutputBaseAdapter;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 09ec4ca..cd26e0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -54,6 +54,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -93,6 +94,7 @@
             mock(NotificationEntryManager.class);
     private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
     private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+    private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
 
     private Context mSpyContext;
     private MediaOutputController mMediaOutputController;
@@ -115,7 +117,7 @@
 
         mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
         mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         MediaDescription.Builder builder = new MediaDescription.Builder();
@@ -159,7 +161,7 @@
     public void start_withoutPackageName_verifyMediaControllerInit() {
         mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
 
         mMediaOutputController.start(mCb);
 
@@ -180,7 +182,7 @@
     public void stop_withoutPackageName_verifyMediaControllerDeinit() {
         mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
 
         mMediaOutputController.start(mCb);
 
@@ -451,7 +453,7 @@
     public void getNotificationLargeIcon_withoutPackageName_returnsNull() {
         mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
 
         assertThat(mMediaOutputController.getNotificationIcon()).isNull();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 8a3ea56..ada8d35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -40,6 +40,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import org.junit.After;
 import org.junit.Before;
@@ -67,6 +68,7 @@
             mock(NotificationEntryManager.class);
     private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
     private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+    private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
 
     private MediaOutputDialog mMediaOutputDialog;
     private MediaOutputController mMediaOutputController;
@@ -76,10 +78,10 @@
     public void setUp() {
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         mMediaOutputDialog = new MediaOutputDialog(mContext, false,
-                mMediaOutputController, mUiEventLogger);
+                mMediaOutputController, mUiEventLogger, mDialogManager);
         mMediaOutputDialog.show();
 
         when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice);
@@ -125,7 +127,7 @@
     // and verify if the calling times increases.
     public void onCreate_ShouldLogVisibility() {
         MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false,
-                mMediaOutputController, mUiEventLogger);
+                mMediaOutputController, mUiEventLogger, mDialogManager);
         testDialog.show();
 
         testDialog.dismissDialog();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
index e8cd6c8..b114452 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
@@ -38,6 +38,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import org.junit.After;
 import org.junit.Before;
@@ -66,6 +67,7 @@
             mock(NotificationEntryManager.class);
     private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
     private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+    private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
 
     private MediaOutputGroupDialog mMediaOutputGroupDialog;
     private MediaOutputController mMediaOutputController;
@@ -75,10 +77,10 @@
     public void setUp() {
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false,
-                mMediaOutputController);
+                mMediaOutputController, mDialogManager);
         mMediaOutputGroupDialog.show();
         when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 9d2541c..3e8e874 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -47,6 +47,7 @@
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.pip.Pip;
 
 import org.junit.After;
 import org.junit.Before;
@@ -55,6 +56,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Optional;
+
 /** atest NavigationBarControllerTest */
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
@@ -88,7 +91,8 @@
                         mNavigationBarFactory,
                         mock(DumpManager.class),
                         mock(AutoHideController.class),
-                        mock(LightBarController.class)));
+                        mock(LightBarController.class),
+                        Optional.of(mock(Pip.class))));
         initializeNavigationBars();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
index 77946cf..d3bb241 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -1,5 +1,7 @@
 package com.android.systemui.qs.tiles.dialog;
 
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -83,7 +85,7 @@
 
     @Test
     public void getItemCount_returnWifiEntriesCount() {
-        for (int i = 0; i < InternetDialogController.MAX_WIFI_ENTRY_COUNT; i++) {
+        for (int i = 0; i < MAX_WIFI_ENTRY_COUNT; i++) {
             mInternetAdapter.setWifiEntries(mWifiEntries, i /* wifiEntriesCount */);
 
             assertThat(mInternetAdapter.getItemCount()).isEqualTo(i);
@@ -141,6 +143,60 @@
     }
 
     @Test
+    public void setWifiEntries_wifiCountLessThenMaxCount_setWifiCount() {
+        final int wifiCount = MAX_WIFI_ENTRY_COUNT - 1;
+        mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT;
+
+        mInternetAdapter.setWifiEntries(mWifiEntries, wifiCount);
+
+        assertThat(mInternetAdapter.mWifiEntriesCount).isEqualTo(wifiCount);
+    }
+
+    @Test
+    public void setWifiEntries_wifiCountGreaterThenMaxCount_setMaxCount() {
+        final int wifiCount = MAX_WIFI_ENTRY_COUNT;
+        mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT - 1;
+
+        mInternetAdapter.setWifiEntries(mWifiEntries, wifiCount);
+
+        assertThat(mInternetAdapter.mWifiEntriesCount).isEqualTo(mInternetAdapter.mMaxEntriesCount);
+    }
+
+    @Test
+    public void setMaxEntriesCount_maxCountLessThenZero_doNothing() {
+        mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT;
+        final int maxCount = -1;
+
+        mInternetAdapter.setMaxEntriesCount(maxCount);
+
+        assertThat(mInternetAdapter.mMaxEntriesCount).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+    }
+
+    @Test
+    public void setMaxEntriesCount_maxCountGreaterThenWifiCount_updateMaxCount() {
+        mInternetAdapter.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 2;
+        mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT;
+        final int maxCount = MAX_WIFI_ENTRY_COUNT - 1;
+
+        mInternetAdapter.setMaxEntriesCount(maxCount);
+
+        assertThat(mInternetAdapter.mWifiEntriesCount).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+        assertThat(mInternetAdapter.mMaxEntriesCount).isEqualTo(maxCount);
+    }
+
+    @Test
+    public void setMaxEntriesCount_maxCountLessThenWifiCount_updateBothCount() {
+        mInternetAdapter.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
+        mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT;
+        final int maxCount = MAX_WIFI_ENTRY_COUNT - 1;
+
+        mInternetAdapter.setMaxEntriesCount(maxCount);
+
+        assertThat(mInternetAdapter.mWifiEntriesCount).isEqualTo(maxCount);
+        assertThat(mInternetAdapter.mMaxEntriesCount).isEqualTo(maxCount);
+    }
+
+    @Test
     public void viewHolderUpdateEndIcon_wifiConnected_updateGearIcon() {
         mTestableResources.addOverride(GEAR_ICON_RES_ID, mGearIcon);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 95e7a98c..0d65541 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -159,7 +159,6 @@
         mAccessPoints.add(mWifiEntry1);
         when(mAccessPointController.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry);
         when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{SUB_ID});
-        when(mAccessPointController.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry);
         when(mToastFactory.createToast(any(), anyString(), anyString(), anyInt(), anyInt()))
             .thenReturn(mSystemUIToast);
         when(mSystemUIToast.getView()).thenReturn(mToastView);
@@ -236,10 +235,18 @@
     }
 
     @Test
-    public void getSubtitleText_withAirplaneModeOn_returnNull() {
+    public void getSubtitleText_withApmOnAndWifiOff_returnWifiIsOff() {
         fakeAirplaneModeEnabled(true);
+        when(mWifiManager.isWifiEnabled()).thenReturn(false);
 
-        assertThat(mInternetDialogController.getSubtitleText(false)).isNull();
+        assertThat(mInternetDialogController.getSubtitleText(false))
+                .isEqualTo(getResourcesString("wifi_is_off"));
+
+        // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
+        mInternetDialogController.mCanConfigWifi = false;
+
+        assertThat(mInternetDialogController.getSubtitleText(false))
+                .isNotEqualTo(getResourcesString("wifi_is_off"));
     }
 
     @Test
@@ -335,6 +342,17 @@
     }
 
     @Test
+    public void getSubtitleText_withCarrierNetworkActiveOnly_returnNoOtherAvailable() {
+        fakeAirplaneModeEnabled(false);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+        mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+        when(mMergedCarrierEntry.isDefaultNetwork()).thenReturn(true);
+
+        assertThat(mInternetDialogController.getSubtitleText(false))
+                .isEqualTo(getResourcesString("non_carrier_network_unavailable"));
+    }
+
+    @Test
     public void getWifiDetailsSettingsIntent_withNoKey_returnNull() {
         assertThat(mInternetDialogController.getWifiDetailsSettingsIntent(null)).isNull();
     }
@@ -400,7 +418,7 @@
 
         mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
 
-        verify(mInternetDialogCallback, never()).onAccessPointsChanged(any(), any());
+        verify(mInternetDialogCallback, never()).onAccessPointsChanged(any(), any(), anyBoolean());
     }
 
     @Test
@@ -409,27 +427,26 @@
 
         mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
 
-        verify(mInternetDialogCallback)
-                .onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */);
+        verify(mInternetDialogCallback).onAccessPointsChanged(null /* wifiEntries */,
+                null /* connectedEntry */, false /* hasMoreEntry */);
     }
 
     @Test
     public void onAccessPointsChanged_oneConnectedEntry_callbackConnectedEntryOnly() {
         reset(mInternetDialogCallback);
-        fakeAirplaneModeEnabled(true);
         mAccessPoints.clear();
         mAccessPoints.add(mConnectedEntry);
 
         mInternetDialogController.onAccessPointsChanged(mAccessPoints);
 
         mWifiEntries.clear();
-        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
+                false /* hasMoreEntry */);
     }
 
     @Test
     public void onAccessPointsChanged_noConnectedEntryAndOneOther_callbackWifiEntriesOnly() {
         reset(mInternetDialogCallback);
-        fakeAirplaneModeEnabled(true);
         mAccessPoints.clear();
         mAccessPoints.add(mWifiEntry1);
 
@@ -437,14 +454,13 @@
 
         mWifiEntries.clear();
         mWifiEntries.add(mWifiEntry1);
-        verify(mInternetDialogCallback)
-                .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries,
+                null /* connectedEntry */, false /* hasMoreEntry */);
     }
 
     @Test
     public void onAccessPointsChanged_oneConnectedEntryAndOneOther_callbackCorrectly() {
         reset(mInternetDialogCallback);
-        fakeAirplaneModeEnabled(true);
         mAccessPoints.clear();
         mAccessPoints.add(mConnectedEntry);
         mAccessPoints.add(mWifiEntry1);
@@ -453,13 +469,13 @@
 
         mWifiEntries.clear();
         mWifiEntries.add(mWifiEntry1);
-        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
+                false /* hasMoreEntry */);
     }
 
     @Test
     public void onAccessPointsChanged_oneConnectedEntryAndTwoOthers_callbackCorrectly() {
         reset(mInternetDialogCallback);
-        fakeAirplaneModeEnabled(true);
         mAccessPoints.clear();
         mAccessPoints.add(mConnectedEntry);
         mAccessPoints.add(mWifiEntry1);
@@ -470,13 +486,13 @@
         mWifiEntries.clear();
         mWifiEntries.add(mWifiEntry1);
         mWifiEntries.add(mWifiEntry2);
-        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
+                false /* hasMoreEntry */);
     }
 
     @Test
     public void onAccessPointsChanged_oneConnectedEntryAndThreeOthers_callbackCutMore() {
         reset(mInternetDialogCallback);
-        fakeAirplaneModeEnabled(true);
         mAccessPoints.clear();
         mAccessPoints.add(mConnectedEntry);
         mAccessPoints.add(mWifiEntry1);
@@ -488,52 +504,13 @@
         mWifiEntries.clear();
         mWifiEntries.add(mWifiEntry1);
         mWifiEntries.add(mWifiEntry2);
-        mWifiEntries.add(mWifiEntry3);
-        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
-
-        // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
-        reset(mInternetDialogCallback);
-        fakeAirplaneModeEnabled(false);
-
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-
-        mWifiEntries.remove(mWifiEntry3);
-        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
-    }
-
-    @Test
-    public void onAccessPointsChanged_oneConnectedEntryAndFourOthers_callbackCutMore() {
-        reset(mInternetDialogCallback);
-        fakeAirplaneModeEnabled(true);
-        mAccessPoints.clear();
-        mAccessPoints.add(mConnectedEntry);
-        mAccessPoints.add(mWifiEntry1);
-        mAccessPoints.add(mWifiEntry2);
-        mAccessPoints.add(mWifiEntry3);
-        mAccessPoints.add(mWifiEntry4);
-
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-
-        mWifiEntries.clear();
-        mWifiEntries.add(mWifiEntry1);
-        mWifiEntries.add(mWifiEntry2);
-        mWifiEntries.add(mWifiEntry3);
-        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
-
-        // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
-        reset(mInternetDialogCallback);
-        fakeAirplaneModeEnabled(false);
-
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-
-        mWifiEntries.remove(mWifiEntry3);
-        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
+                true /* hasMoreEntry */);
     }
 
     @Test
     public void onAccessPointsChanged_fourWifiEntries_callbackCutMore() {
         reset(mInternetDialogCallback);
-        fakeAirplaneModeEnabled(true);
         mAccessPoints.clear();
         mAccessPoints.add(mWifiEntry1);
         mAccessPoints.add(mWifiEntry2);
@@ -546,29 +523,8 @@
         mWifiEntries.add(mWifiEntry1);
         mWifiEntries.add(mWifiEntry2);
         mWifiEntries.add(mWifiEntry3);
-        mWifiEntries.add(mWifiEntry4);
-        verify(mInternetDialogCallback)
-                .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
-
-        // If the Ethernet exists, then Wi-Fi entries will cut last one.
-        reset(mInternetDialogCallback);
-        mInternetDialogController.mHasEthernet = true;
-
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-
-        mWifiEntries.remove(mWifiEntry4);
-        verify(mInternetDialogCallback)
-                .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
-
-        // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
-        reset(mInternetDialogCallback);
-        fakeAirplaneModeEnabled(false);
-
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-
-        mWifiEntries.remove(mWifiEntry3);
-        verify(mInternetDialogCallback)
-                .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries,
+                null /* connectedEntry */, true /* hasMoreEntry */);
     }
 
     @Test
@@ -584,8 +540,39 @@
 
         mWifiEntries.clear();
         mWifiEntries.add(mWifiEntry1);
-        verify(mInternetDialogCallback)
-                .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries,
+                null /* connectedEntry */, false /* hasMoreEntry */);
+    }
+
+    @Test
+    public void onAccessPointsChanged_connectedWifiNoInternetAccess_shouldSetListener() {
+        reset(mWifiEntry1);
+        mAccessPoints.clear();
+        when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+        when(mWifiEntry1.isDefaultNetwork()).thenReturn(true);
+        when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
+        mAccessPoints.add(mWifiEntry1);
+
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+        verify(mWifiEntry1).setListener(mInternetDialogController.mConnectedWifiInternetMonitor);
+    }
+
+    @Test
+    public void onUpdated_connectedWifiHasInternetAccess_shouldScanWifiAccessPoints() {
+        reset(mAccessPointController);
+        when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+        when(mWifiEntry1.isDefaultNetwork()).thenReturn(true);
+        when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
+        InternetDialogController.ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor =
+                mInternetDialogController.mConnectedWifiInternetMonitor;
+        mConnectedWifiInternetMonitor.registerCallbackIfNeed(mWifiEntry1);
+
+        // When the hasInternetAccess() changed to true, and call back the onUpdated() function.
+        when(mWifiEntry1.hasInternetAccess()).thenReturn(true);
+        mConnectedWifiInternetMonitor.onUpdated();
+
+        verify(mAccessPointController).scanForAccessPoints();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index 0cf063f..c20e887 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -140,7 +140,7 @@
 
         mInternetDialog.updateDialog(true);
 
-        assertThat(mSubTitle.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
     }
 
     @Test
@@ -312,6 +312,22 @@
         assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
         // Show a blank block to fix the dialog height even if there is no WiFi list
         assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+        verify(mInternetAdapter).setMaxEntriesCount(3);
+        assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
+    }
+
+    @Test
+    public void updateDialog_wifiOnAndOneWifiEntry_showWifiListAndSeeAllArea() {
+        // The precondition WiFi ON is already in setUp()
+        mInternetDialog.mConnectedWifiEntry = null;
+        mInternetDialog.mWifiEntriesCount = 1;
+
+        mInternetDialog.updateDialog(false);
+
+        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+        // Show a blank block to fix the dialog height even if there is no WiFi list
+        assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+        verify(mInternetAdapter).setMaxEntriesCount(3);
         assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
     }
 
@@ -325,29 +341,36 @@
         assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
         // Show a blank block to fix the dialog height even if there is no WiFi list
         assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
+        verify(mInternetAdapter).setMaxEntriesCount(2);
+        assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
     }
 
     @Test
-    public void updateDialog_wifiOnAndHasWifiList_showWifiListAndSeeAll() {
+    public void updateDialog_wifiOnAndHasMaxWifiList_showWifiListAndSeeAll() {
         // The preconditions WiFi ON and WiFi entries are already in setUp()
         mInternetDialog.mConnectedWifiEntry = null;
+        mInternetDialog.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
+        mInternetDialog.mHasMoreWifiEntries = true;
 
         mInternetDialog.updateDialog(false);
 
         assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
         assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+        verify(mInternetAdapter).setMaxEntriesCount(3);
         assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
     }
 
     @Test
     public void updateDialog_wifiOnAndHasBothWifiEntry_showBothWifiEntryAndSeeAll() {
         // The preconditions WiFi ON and WiFi entries are already in setUp()
+        mInternetDialog.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 1;
+        mInternetDialog.mHasMoreWifiEntries = true;
 
         mInternetDialog.updateDialog(false);
 
         assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+        verify(mInternetAdapter).setMaxEntriesCount(2);
         assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
     }
 
@@ -500,45 +523,46 @@
 
     @Test
     public void getWifiListMaxCount_returnCountCorrectly() {
-        // Ethernet, MobileData, ConnectedWiFi are all hidden.
+        // Both of the Ethernet, MobileData is hidden.
         // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
         setNetworkVisible(false, false, false);
 
         assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
 
-        // Only one of Ethernet, MobileData, ConnectedWiFi is displayed.
-        // Then the maximum count  is equal to MAX_WIFI_ENTRY_COUNT - 1.
-        setNetworkVisible(true, false, false);
-
-        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
-
-        setNetworkVisible(false, true, false);
-
-        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
-
+        // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
         setNetworkVisible(false, false, true);
 
         assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
 
-        // Only one of Ethernet, MobileData, ConnectedWiFi is hidden.
-        // Then the maximum count  is equal to MAX_WIFI_ENTRY_COUNT - 2.
-        setNetworkVisible(true, true, false);
+        // Only one of Ethernet, MobileData is displayed.
+        // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
+        setNetworkVisible(true, false, false);
 
-        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
 
+        setNetworkVisible(false, true, false);
+
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+
+        // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
         setNetworkVisible(true, false, true);
 
-        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
 
         setNetworkVisible(false, true, true);
 
-        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
 
-        // Ethernet, MobileData, ConnectedWiFi are all displayed.
-        // Then the maximum count  is equal to MAX_WIFI_ENTRY_COUNT - 3.
+        // Both of Ethernet, MobileData, ConnectedWiFi is displayed.
+        // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 1.
+        setNetworkVisible(true, true, false);
+
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+        // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
         setNetworkVisible(true, true, true);
 
-        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 3);
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
     }
 
     private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 7938511..89435ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -6,6 +6,7 @@
 import android.testing.TestableLooper.RunWithLooper
 import android.util.DisplayMetrics
 import com.android.systemui.ExpandHelper
+import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.media.MediaHierarchyManager
@@ -81,6 +82,8 @@
                 mDependency,
                 TestableLooper.get(this))
         row = helper.createRow()
+        context.getOrCreateTestableResources()
+                .addOverride(R.bool.config_use_split_notification_shade, false)
         transitionController = LockscreenShadeTransitionController(
             statusBarStateController = statusbarStateController,
             lockscreenGestureLogger = lockscreenGestureLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 5b60c9e..ea68143 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -4,6 +4,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.EmptyShadeView
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
@@ -55,4 +56,24 @@
         // top margin presence should decrease heads up translation up to minHeadsUpTranslation
         assertThat(expandableViewState.yTranslation).isEqualTo(minHeadsUpTranslation)
     }
-}
\ No newline at end of file
+
+    @Test
+    fun resetViewStates_childIsEmptyShadeView_viewIsCenteredVertically() {
+        stackScrollAlgorithm.initView(context)
+        val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
+            layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
+        }
+        hostView.removeAllViews()
+        hostView.addView(emptyShadeView)
+        ambientState.layoutMaxHeight = 1280
+
+        stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+        val closeHandleUnderlapHeight =
+            context.resources.getDimensionPixelSize(R.dimen.close_handle_underlap)
+        val fullHeight =
+            ambientState.layoutMaxHeight + closeHandleUnderlapHeight - ambientState.stackY
+        val centeredY = ambientState.stackY + fullHeight / 2f - emptyShadeView.height / 2f
+        assertThat(emptyShadeView.viewState?.yTranslation).isEqualTo(centeredY)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 07debe6..c3349f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -381,16 +381,15 @@
     }
 
     @Test
-    public void onUdfpsConsecutivelyFailedThreeTimes_showBouncer() {
+    public void onUdfpsConsecutivelyFailedTwoTimes_showBouncer() {
         // GIVEN UDFPS is supported
         when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true);
 
-        // WHEN udfps fails twice - then don't show the bouncer
-        mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+        // WHEN udfps fails once - then don't show the bouncer
         mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
         verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
 
-        // WHEN udfps fails the third time
+        // WHEN udfps fails the second time
         mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
 
         // THEN show the bouncer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
index 2e7f8a2..e45fa77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
@@ -1,8 +1,8 @@
 package com.android.systemui.statusbar.phone
 
-import android.test.suitebuilder.annotation.SmallTest
 import android.testing.AndroidTestingRunner
 import android.view.View
+import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ShadeInterpolation
@@ -41,6 +41,7 @@
     var viewVisibility = View.GONE
 
     private lateinit var splitShadeHeaderController: SplitShadeHeaderController
+    private lateinit var carrierIconSlots: List<String>
 
     @Before
     fun setup() {
@@ -66,12 +67,13 @@
                 featureFlags,
                 batteryMeterViewController
         )
+        carrierIconSlots = listOf(
+                context.getString(com.android.internal.R.string.status_bar_mobile))
     }
 
     @Test
     fun setVisible_onlyInSplitShade() {
-        splitShadeHeaderController.splitShadeMode = true
-        splitShadeHeaderController.shadeExpanded = true
+        makeShadeVisible()
         assertThat(viewVisibility).isEqualTo(View.VISIBLE)
 
         splitShadeHeaderController.splitShadeMode = false
@@ -80,17 +82,38 @@
 
     @Test
     fun updateListeners_registersWhenVisible() {
-        splitShadeHeaderController.splitShadeMode = true
-        splitShadeHeaderController.shadeExpanded = true
+        makeShadeVisible()
         verify(qsCarrierGroupController).setListening(true)
         verify(statusBarIconController).addIconGroup(any())
     }
 
     @Test
     fun shadeExpandedFraction_updatesAlpha() {
-        splitShadeHeaderController.splitShadeMode = true
-        splitShadeHeaderController.shadeExpanded = true
+        makeShadeVisible()
         splitShadeHeaderController.shadeExpandedFraction = 0.5f
         verify(view).setAlpha(ShadeInterpolation.getContentAlpha(0.5f))
     }
-}
\ No newline at end of file
+
+    @Test
+    fun singleCarrier_enablesCarrierIconsInStatusIcons() {
+        whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
+
+        makeShadeVisible()
+
+        verify(statusIcons).removeIgnoredSlots(carrierIconSlots)
+    }
+
+    @Test
+    fun dualCarrier_disablesCarrierIconsInStatusIcons() {
+        whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(false)
+
+        makeShadeVisible()
+
+        verify(statusIcons).addIgnoredSlots(carrierIconSlots)
+    }
+
+    private fun makeShadeVisible() {
+        splitShadeHeaderController.splitShadeMode = true
+        splitShadeHeaderController.shadeExpanded = true
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index c5bdfed..cc59b6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -389,6 +389,39 @@
     }
 
     @Test
+    public void testShowAltAuth_unlockingWithBiometricNotAllowed() {
+        // GIVEN alt auth exists, unlocking with biometric isn't allowed
+        mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+        when(mBouncer.isShowing()).thenReturn(false);
+        when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+                .thenReturn(false);
+
+        // WHEN showGenericBouncer is called
+        final boolean scrimmed = true;
+        mStatusBarKeyguardViewManager.showGenericBouncer(scrimmed);
+
+        // THEN regular bouncer is shown
+        verify(mBouncer).show(anyBoolean(), eq(scrimmed));
+        verify(mAlternateAuthInterceptor, never()).showAlternateAuthBouncer();
+    }
+
+    @Test
+    public void testShowAltAuth_unlockingWithBiometricAllowed() {
+        // GIVEN alt auth exists, unlocking with biometric is allowed
+        mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+        when(mBouncer.isShowing()).thenReturn(false);
+        when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+                .thenReturn(true);
+
+        // WHEN showGenericBouncer is called
+        mStatusBarKeyguardViewManager.showGenericBouncer(true);
+
+        // THEN alt auth bouncer is shown
+        verify(mAlternateAuthInterceptor).showAlternateAuthBouncer();
+        verify(mBouncer, never()).show(anyBoolean(), anyBoolean());
+    }
+
+    @Test
     public void testUpdateResources_delegatesToBouncer() {
         mStatusBarKeyguardViewManager.updateResources();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index a1d9a7b..be1720d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -18,7 +18,9 @@
 
 import android.hardware.devicestate.DeviceStateManager
 import android.hardware.devicestate.DeviceStateManager.FoldStateListener
+import android.os.Handler
 import android.testing.AndroidTestingRunner
+import androidx.core.util.Consumer
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
@@ -31,9 +33,12 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
+import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import java.lang.Exception
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
@@ -48,16 +53,28 @@
     @Mock
     private lateinit var deviceStateManager: DeviceStateManager
 
-    private lateinit var foldStateProvider: FoldStateProvider
+    @Mock
+    private lateinit var handler: Handler
+
+    @Captor
+    private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
+
+    @Captor
+    private lateinit var screenOnListenerCaptor: ArgumentCaptor<ScreenListener>
+
+    @Captor
+    private lateinit var hingeAngleCaptor: ArgumentCaptor<Consumer<Float>>
+
+    private lateinit var foldStateProvider: DeviceFoldStateProvider
 
     private val foldUpdates: MutableList<Int> = arrayListOf()
     private val hingeAngleUpdates: MutableList<Float> = arrayListOf()
 
-    private val foldStateListenerCaptor = ArgumentCaptor.forClass(FoldStateListener::class.java)
     private var foldedDeviceState: Int = 0
     private var unfoldedDeviceState: Int = 0
 
-    private val screenOnListenerCaptor = ArgumentCaptor.forClass(ScreenListener::class.java)
+    private var scheduledRunnable: Runnable? = null
+    private var scheduledRunnableDelay: Long? = null
 
     @Before
     fun setUp() {
@@ -75,7 +92,8 @@
             hingeAngleProvider,
             screenStatusProvider,
             deviceStateManager,
-            context.mainExecutor
+            context.mainExecutor,
+            handler
         )
 
         foldStateProvider.addCallback(object : FoldStateProvider.FoldUpdatesListener {
@@ -91,6 +109,22 @@
 
         verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
         verify(screenStatusProvider).addCallback(screenOnListenerCaptor.capture())
+        verify(hingeAngleProvider).addCallback(hingeAngleCaptor.capture())
+
+        whenever(handler.postDelayed(any<Runnable>(), any())).then { invocationOnMock ->
+            scheduledRunnable = invocationOnMock.getArgument<Runnable>(0)
+            scheduledRunnableDelay = invocationOnMock.getArgument<Long>(1)
+            null
+        }
+
+        whenever(handler.removeCallbacks(any<Runnable>())).then { invocationOnMock ->
+            val removedRunnable = invocationOnMock.getArgument<Runnable>(0)
+            if (removedRunnable == scheduledRunnable) {
+                scheduledRunnableDelay = null
+                scheduledRunnable = null
+            }
+            null
+        }
     }
 
     @Test
@@ -167,6 +201,86 @@
         assertThat(foldUpdates).isEmpty()
     }
 
+    @Test
+    fun startClosingEvent_afterTimeout_abortEmitted() {
+        sendHingeAngleEvent(90)
+        sendHingeAngleEvent(80)
+
+        simulateTimeout(ABORT_CLOSING_MILLIS)
+
+        assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_ABORTED)
+    }
+
+    @Test
+    fun startClosingEvent_beforeTimeout_abortNotEmitted() {
+        sendHingeAngleEvent(90)
+        sendHingeAngleEvent(80)
+
+        simulateTimeout(ABORT_CLOSING_MILLIS - 1)
+
+        assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+    }
+
+    @Test
+    fun startClosingEvent_eventBeforeTimeout_oneEventEmitted() {
+        sendHingeAngleEvent(180)
+        sendHingeAngleEvent(90)
+
+        simulateTimeout(ABORT_CLOSING_MILLIS - 1)
+        sendHingeAngleEvent(80)
+
+        assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+    }
+
+    @Test
+    fun startClosingEvent_timeoutAfterTimeoutRescheduled_abortEmitted() {
+        sendHingeAngleEvent(180)
+        sendHingeAngleEvent(90)
+
+        simulateTimeout(ABORT_CLOSING_MILLIS - 1) // The timeout should not trigger here.
+        sendHingeAngleEvent(80)
+        simulateTimeout(ABORT_CLOSING_MILLIS) // The timeout should trigger here.
+
+        assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_ABORTED)
+    }
+
+    @Test
+    fun startClosingEvent_shortTimeBetween_emitsOnlyOneEvents() {
+        sendHingeAngleEvent(180)
+
+        sendHingeAngleEvent(90)
+        sendHingeAngleEvent(80)
+
+        assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+    }
+
+    @Test
+    fun startClosingEvent_whileClosing_emittedDespiteInitialAngle() {
+        val maxAngle = 180 - FULLY_OPEN_THRESHOLD_DEGREES.toInt()
+        for (i in 1..maxAngle) {
+            foldUpdates.clear()
+
+            simulateFolding(startAngle = i)
+
+            assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+            simulateTimeout() // Timeout to set the state to aborted.
+        }
+    }
+
+    private fun simulateTimeout(waitTime: Long = ABORT_CLOSING_MILLIS) {
+        val runnableDelay = scheduledRunnableDelay ?: throw Exception("No runnable scheduled.")
+        if (waitTime >= runnableDelay) {
+            scheduledRunnable?.run()
+            scheduledRunnable = null
+            scheduledRunnableDelay = null
+        }
+    }
+
+    private fun simulateFolding(startAngle: Int) {
+        sendHingeAngleEvent(startAngle)
+        sendHingeAngleEvent(startAngle - 1)
+    }
+
     private fun setFoldState(folded: Boolean) {
         val state = if (folded) foldedDeviceState else unfoldedDeviceState
         foldStateListenerCaptor.value.onStateChanged(state)
@@ -175,4 +289,8 @@
     private fun fireScreenOnEvent() {
         screenOnListenerCaptor.value.onScreenTurnedOn()
     }
+
+    private fun sendHingeAngleEvent(angle: Int) {
+        hingeAngleCaptor.value.accept(angle.toFloat())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 1e15d2a..2f2e536 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -35,6 +35,7 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.wm.shell.ShellCommandHandler;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.draganddrop.DragAndDrop;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -42,7 +43,6 @@
 import com.android.wm.shell.onehanded.OneHandedEventCallback;
 import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
 import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
 import com.android.wm.shell.splitscreen.SplitScreen;
 
 import org.junit.Before;
@@ -78,7 +78,7 @@
     @Mock WakefulnessLifecycle mWakefulnessLifecycle;
     @Mock ProtoTracer mProtoTracer;
     @Mock ShellCommandHandler mShellCommandHandler;
-    @Mock SizeCompatUI mSizeCompatUI;
+    @Mock CompatUI mCompatUI;
     @Mock ShellExecutor mSysUiMainExecutor;
     @Mock DragAndDrop mDragAndDrop;
 
@@ -88,7 +88,7 @@
 
         mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen),
                 Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
-                Optional.of(mShellCommandHandler), Optional.of(mSizeCompatUI),
+                Optional.of(mShellCommandHandler), Optional.of(mCompatUI),
                 Optional.of(mDragAndDrop),
                 mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor,
                 mNavigationModeController, mScreenLifecycle, mSysUiState, mProtoTracer,
@@ -136,8 +136,8 @@
     }
 
     @Test
-    public void initSizeCompatUI_registersCallbacks() {
-        mWMShell.initSizeCompatUi(mSizeCompatUI);
+    public void initCompatUI_registersCallbacks() {
+        mWMShell.initCompatUi(mCompatUI);
 
         verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index eaf2694..6744ea8 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -288,8 +288,6 @@
                     showGlobalActions();
                     return true;
                 }
-                case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN:
-                    return toggleSplitScreen();
                 case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN:
                     return lockScreen();
                 case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT:
@@ -369,21 +367,6 @@
         mWindowManagerService.showGlobalActions();
     }
 
-    private boolean toggleSplitScreen() {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            StatusBarManagerInternal statusBarService = LocalServices.getService(
-                    StatusBarManagerInternal.class);
-            if (statusBarService == null) {
-                return false;
-            }
-            statusBarService.toggleSplitScreen();
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-        return true;
-    }
-
     private boolean lockScreen() {
         mContext.getSystemService(PowerManager.class).goToSleep(SystemClock.uptimeMillis(),
                 PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY, 0);
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 9ee0159..0b95fef 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -4068,14 +4068,20 @@
         }
 
         int operationType;
+        TransportClient transportClient = null;
         try {
-            operationType = getOperationTypeFromTransport(
-                    mTransportManager.getTransportClientOrThrow(transport, /* caller */
-                            "BMS.beginRestoreSession"));
+            transportClient = mTransportManager.getTransportClientOrThrow(
+                    transport, /* caller */"BMS.beginRestoreSession");
+            operationType = getOperationTypeFromTransport(transportClient);
         } catch (TransportNotAvailableException | TransportNotRegisteredException
                 | RemoteException e) {
             Slog.w(TAG, "Failed to get operation type from transport: " + e);
             return null;
+        } finally {
+            if (transportClient != null) {
+                mTransportManager.disposeOfTransportClient(transportClient,
+                        /* caller */"BMS.beginRestoreSession");
+            }
         }
 
         synchronized (this) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 53b1608..330d2dd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -92,6 +92,7 @@
 import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
@@ -300,6 +301,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
+import android.util.FeatureFlagUtils;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
@@ -4184,7 +4186,7 @@
 
             didSomething |= mProcessList.killPackageProcessesLSP(packageName, appId, userId,
                     ProcessList.INVALID_ADJ, callerWillRestart, false /* allowRestart */, doit,
-                    evenPersistent, true /* setRemoved */,
+                    evenPersistent, true /* setRemoved */, uninstalling,
                     packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED
                     : ApplicationExitInfo.REASON_USER_REQUESTED,
                     ApplicationExitInfo.SUBREASON_UNKNOWN,
@@ -7220,6 +7222,7 @@
                             ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
                             true /* callerWillRestart */, true /* doit */,
                             true /* evenPersistent */, false /* setRemoved */,
+                            false /* uninstalling */,
                             ApplicationExitInfo.REASON_OTHER,
                             ApplicationExitInfo.SUBREASON_KILL_UID,
                             reason != null ? reason : "kill uid");
@@ -7241,6 +7244,7 @@
                             ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
                             true /* callerWillRestart */, true /* doit */,
                             true /* evenPersistent */, false /* setRemoved */,
+                            false /* uninstalling */,
                             ApplicationExitInfo.REASON_PERMISSION_CHANGE,
                             ApplicationExitInfo.SUBREASON_UNKNOWN,
                             reason != null ? reason : "kill uid");
@@ -14274,6 +14278,8 @@
     private void checkExcessivePowerUsage() {
         updateCpuStatsNow();
 
+        final boolean monitorPhantomProcs = mSystemReady && FeatureFlagUtils.isEnabled(mContext,
+                SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
         synchronized (mProcLock) {
             final boolean doCpuKills = mLastPowerCheckUptime != 0;
             final long curUptime = SystemClock.uptimeMillis();
@@ -14299,9 +14305,11 @@
 
                     updateAppProcessCpuTimeLPr(uptimeSince, doCpuKills, checkDur, cpuLimit, app);
 
-                    // Also check the phantom processes if there is any
-                    updatePhantomProcessCpuTimeLPr(
-                            uptimeSince, doCpuKills, checkDur, cpuLimit, app);
+                    if (monitorPhantomProcs) {
+                        // Also check the phantom processes if there is any
+                        updatePhantomProcessCpuTimeLPr(
+                                uptimeSince, doCpuKills, checkDur, cpuLimit, app);
+                    }
                 }
             });
         }
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index ad0485b..293b8a9 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
 
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
@@ -77,6 +78,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.DebugUtils;
+import android.util.FeatureFlagUtils;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -1783,6 +1785,8 @@
     }
 
     void updateCpuStatsNow() {
+        final boolean monitorPhantomProcs = mService.mSystemReady && FeatureFlagUtils.isEnabled(
+                mService.mContext, SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
         synchronized (mProcessCpuTracker) {
             mProcessCpuMutexFree.set(false);
             final long now = SystemClock.uptimeMillis();
@@ -1821,7 +1825,7 @@
                 }
             }
 
-            if (haveNewCpuStats) {
+            if (monitorPhantomProcs && haveNewCpuStats) {
                 mService.mPhantomProcessList.updateProcessCpuStatesLocked(mProcessCpuTracker);
             }
 
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index ff480d1..7673123 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1633,7 +1633,7 @@
         int schedGroup;
         int procState;
         int cachedAdjSeq;
-        int capability = 0;
+        int capability = cycleReEval ? app.mState.getCurCapability() : 0;
 
         boolean foregroundActivities = false;
         boolean hasVisibleActivities = false;
@@ -2018,10 +2018,6 @@
                     }
 
                     if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) {
-                        if (shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) {
-                            continue;
-                        }
-
                         if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
                             capability |= cstate.getCurCapability();
                         }
@@ -2042,6 +2038,10 @@
                             }
                         }
 
+                        if (shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) {
+                            continue;
+                        }
+
                         if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                             // If the other app is cached for any reason, for purposes here
                             // we are going to consider it empty.  The specific cached state
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index b07684c..2ec1aed 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -18,6 +18,7 @@
 
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -28,6 +29,7 @@
 import android.os.Handler;
 import android.os.Process;
 import android.os.StrictMode;
+import android.util.FeatureFlagUtils;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -419,6 +421,10 @@
      * order of the oom adjs of their parent process.
      */
     void trimPhantomProcessesIfNecessary() {
+        if (!mService.mSystemReady || !FeatureFlagUtils.isEnabled(mService.mContext,
+                SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS)) {
+            return;
+        }
         synchronized (mService.mProcLock) {
             synchronized (mLock) {
                 mTrimPhantomProcessScheduled = false;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b77270f..1e66ed4 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -374,6 +374,16 @@
     private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
 
     /**
+     * Native heap allocations in AppZygote process and its descendants will now have a
+     * non-zero tag in the most significant byte.
+     * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
+     * Pointers</a>
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE = 207557677;
+
+    /**
      * Enable asynchronous (ASYNC) memory tag checking in this process. This
      * flag will only have an effect on hardware supporting the ARM Memory
      * Tagging Extension (MTE).
@@ -1738,6 +1748,16 @@
         return level;
     }
 
+    private int decideTaggingLevelForAppZygote(ProcessRecord app) {
+        int level = decideTaggingLevel(app);
+        // TBI ("fake" pointer tagging) in AppZygote is controlled by a separate compat feature.
+        if (!mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE, app.info)
+                && level == Zygote.MEMORY_TAG_LEVEL_TBI) {
+            level = Zygote.MEMORY_TAG_LEVEL_NONE;
+        }
+        return level;
+    }
+
     private int decideGwpAsanLevel(ProcessRecord app) {
         // Look at the process attribute first.
        if (app.processInfo != null
@@ -2238,7 +2258,8 @@
                 // not the calling one.
                 appInfo.packageName = app.getHostingRecord().getDefiningPackageName();
                 appInfo.uid = uid;
-                appZygote = new AppZygote(appInfo, uid, firstUid, lastUid);
+                int runtimeFlags = decideTaggingLevelForAppZygote(app);
+                appZygote = new AppZygote(appInfo, uid, firstUid, lastUid, runtimeFlags);
                 mAppZygotes.put(app.info.processName, uid, appZygote);
                 zygoteProcessList = new ArrayList<ProcessRecord>();
                 mAppZygoteProcesses.put(appZygote, zygoteProcessList);
@@ -2750,8 +2771,8 @@
             int reasonCode, int subReason, String reason) {
         return killPackageProcessesLSP(packageName, appId, userId, minOomAdj,
                 false /* callerWillRestart */, true /* allowRestart */, true /* doit */,
-                false /* evenPersistent */, false /* setRemoved */, reasonCode,
-                subReason, reason);
+                false /* evenPersistent */, false /* setRemoved */, false /* uninstalling */,
+                reasonCode, subReason, reason);
     }
 
     @GuardedBy("mService")
@@ -2784,9 +2805,10 @@
     @GuardedBy({"mService", "mProcLock"})
     boolean killPackageProcessesLSP(String packageName, int appId,
             int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
-            boolean doit, boolean evenPersistent, boolean setRemoved, int reasonCode,
-            int subReason, String reason) {
-        ArrayList<ProcessRecord> procs = new ArrayList<>();
+            boolean doit, boolean evenPersistent, boolean setRemoved, boolean uninstalling,
+            int reasonCode, int subReason, String reason) {
+        final PackageManagerInternal pm = mService.getPackageManagerInternal();
+        final ArrayList<Pair<ProcessRecord, Boolean>> procs = new ArrayList<>();
 
         // Remove all processes this package may have touched: all with the
         // same UID (except for the system or root user), and all whose name
@@ -2803,7 +2825,18 @@
                 }
                 if (app.isRemoved()) {
                     if (doit) {
-                        procs.add(app);
+                        boolean shouldAllowRestart = false;
+                        if (!uninstalling && packageName != null) {
+                            // This package has a dependency on the given package being stopped,
+                            // while it's not being frozen nor uninstalled, allow to restart it.
+                            shouldAllowRestart = !app.getPkgList().containsKey(packageName)
+                                    && app.getPkgDeps() != null
+                                    && app.getPkgDeps().contains(packageName)
+                                    && app.info != null
+                                    && !pm.isPackageFrozen(app.info.packageName, app.uid,
+                                            app.userId);
+                        }
+                        procs.add(new Pair<>(app, shouldAllowRestart));
                     }
                     continue;
                 }
@@ -2818,6 +2851,8 @@
                     continue;
                 }
 
+                boolean shouldAllowRestart = false;
+
                 // If no package is specified, we call all processes under the
                 // give user id.
                 if (packageName == null) {
@@ -2839,9 +2874,16 @@
                     if (userId != UserHandle.USER_ALL && app.userId != userId) {
                         continue;
                     }
-                    if (!app.getPkgList().containsKey(packageName) && !isDep) {
+                    final boolean isInPkgList = app.getPkgList().containsKey(packageName);
+                    if (!isInPkgList && !isDep) {
                         continue;
                     }
+                    if (!isInPkgList && isDep && !uninstalling && app.info != null
+                            && !pm.isPackageFrozen(app.info.packageName, app.uid, app.userId)) {
+                        // This package has a dependency on the given package being stopped,
+                        // while it's not being frozen nor uninstalled, allow to restart it.
+                        shouldAllowRestart = true;
+                    }
                 }
 
                 // Process has passed all conditions, kill it!
@@ -2851,14 +2893,15 @@
                 if (setRemoved) {
                     app.setRemoved(true);
                 }
-                procs.add(app);
+                procs.add(new Pair<>(app, shouldAllowRestart));
             }
         }
 
         int N = procs.size();
         for (int i=0; i<N; i++) {
-            removeProcessLocked(procs.get(i), callerWillRestart, allowRestart,
-                    reasonCode, subReason, reason);
+            final Pair<ProcessRecord, Boolean> proc = procs.get(i);
+            removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second,
+                     reasonCode, subReason, reason);
         }
         killAppZygotesLocked(packageName, appId, userId, false /* force */);
         mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 5ecdfe4..f053e94 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -328,7 +328,7 @@
         }
 
         boolean isBtScoRequested = isBluetoothScoRequested();
-        if (isBtScoRequested && !wasBtScoRequested) {
+        if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) {
             if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
                 Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: "
                         + pid);
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index f42870b..758cf7a 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -1036,7 +1036,8 @@
         promptInfo.setAuthenticators(authenticators);
 
         return PreAuthInfo.create(mTrustManager, mDevicePolicyManager, mSettingObserver, mSensors,
-                userId, promptInfo, opPackageName, false /* checkDevicePolicyManager */);
+                userId, promptInfo, opPackageName, false /* checkDevicePolicyManager */,
+                getContext());
     }
 
     /**
@@ -1375,7 +1376,8 @@
             try {
                 final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager,
                         mDevicePolicyManager, mSettingObserver, mSensors, userId, promptInfo,
-                        opPackageName, promptInfo.isDisallowBiometricsIfPolicyExists());
+                        opPackageName, promptInfo.isDisallowBiometricsIfPolicyExists(),
+                        getContext());
 
                 final Pair<Integer, Integer> preAuthStatus = preAuthInfo.getPreAuthenticateStatus();
 
@@ -1383,8 +1385,11 @@
                         + "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo
                         + " requestId: " + requestId + " promptInfo.isIgnoreEnrollmentState: "
                         + promptInfo.isIgnoreEnrollmentState());
-
-                if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) {
+                // BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED is added so that BiometricPrompt can
+                // be shown for this case.
+                if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS
+                        || preAuthStatus.second
+                        == BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED) {
                     // If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but
                     // CREDENTIAL is requested and available, set the bundle to only request
                     // CREDENTIAL.
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index a5a3542..05c3f68 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -26,6 +26,8 @@
 import android.annotation.NonNull;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.ITrustManager;
+import android.content.Context;
+import android.hardware.SensorPrivacyManager;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.PromptInfo;
@@ -59,6 +61,7 @@
     static final int CREDENTIAL_NOT_ENROLLED = 9;
     static final int BIOMETRIC_LOCKOUT_TIMED = 10;
     static final int BIOMETRIC_LOCKOUT_PERMANENT = 11;
+    static final int BIOMETRIC_SENSOR_PRIVACY_ENABLED = 12;
     @IntDef({AUTHENTICATOR_OK,
             BIOMETRIC_NO_HARDWARE,
             BIOMETRIC_DISABLED_BY_DEVICE_POLICY,
@@ -69,7 +72,8 @@
             BIOMETRIC_NOT_ENABLED_FOR_APPS,
             CREDENTIAL_NOT_ENROLLED,
             BIOMETRIC_LOCKOUT_TIMED,
-            BIOMETRIC_LOCKOUT_PERMANENT})
+            BIOMETRIC_LOCKOUT_PERMANENT,
+            BIOMETRIC_SENSOR_PRIVACY_ENABLED})
     @Retention(RetentionPolicy.SOURCE)
     @interface AuthenticatorStatus {}
 
@@ -84,13 +88,15 @@
     final boolean credentialAvailable;
     final boolean confirmationRequested;
     final boolean ignoreEnrollmentState;
+    final int userId;
+    final Context context;
 
     static PreAuthInfo create(ITrustManager trustManager,
             DevicePolicyManager devicePolicyManager,
             BiometricService.SettingObserver settingObserver,
             List<BiometricSensor> sensors,
             int userId, PromptInfo promptInfo, String opPackageName,
-            boolean checkDevicePolicyManager)
+            boolean checkDevicePolicyManager, Context context)
             throws RemoteException {
 
         final boolean confirmationRequested = promptInfo.isConfirmationRequested();
@@ -116,14 +122,22 @@
                         devicePolicyManager, settingObserver, sensor, userId, opPackageName,
                         checkDevicePolicyManager, requestedStrength,
                         promptInfo.getAllowedSensorIds(),
-                        promptInfo.isIgnoreEnrollmentState());
+                        promptInfo.isIgnoreEnrollmentState(),
+                        context);
 
                 Slog.d(TAG, "Package: " + opPackageName
                         + " Sensor ID: " + sensor.id
                         + " Modality: " + sensor.modality
                         + " Status: " + status);
 
-                if (status == AUTHENTICATOR_OK) {
+                // A sensor with privacy enabled will still be eligible to
+                // authenticate with biometric prompt. This is so the framework can display
+                // a sensor privacy error message to users after briefly showing the
+                // Biometric Prompt.
+                //
+                // Note: if only a certain sensor is required and the privacy is enabled,
+                // canAuthenticate() will return false.
+                if (status == AUTHENTICATOR_OK || status == BIOMETRIC_SENSOR_PRIVACY_ENABLED) {
                     eligibleSensors.add(sensor);
                 } else {
                     ineligibleSensors.add(new Pair<>(sensor, status));
@@ -133,7 +147,7 @@
 
         return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
                 eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested,
-                promptInfo.isIgnoreEnrollmentState());
+                promptInfo.isIgnoreEnrollmentState(), userId, context);
     }
 
     /**
@@ -149,7 +163,7 @@
             BiometricSensor sensor, int userId, String opPackageName,
             boolean checkDevicePolicyManager, int requestedStrength,
             @NonNull List<Integer> requestedSensorIds,
-            boolean ignoreEnrollmentState) {
+            boolean ignoreEnrollmentState, Context context) {
 
         if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) {
             return BIOMETRIC_NO_HARDWARE;
@@ -175,6 +189,16 @@
                     && !ignoreEnrollmentState) {
                 return BIOMETRIC_NOT_ENROLLED;
             }
+            final SensorPrivacyManager sensorPrivacyManager = context
+                    .getSystemService(SensorPrivacyManager.class);
+
+            if (sensorPrivacyManager != null && sensor.modality == TYPE_FACE) {
+                if (sensorPrivacyManager
+                        .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId)) {
+                    return BIOMETRIC_SENSOR_PRIVACY_ENABLED;
+                }
+            }
+
 
             final @LockoutTracker.LockoutMode int lockoutMode =
                     sensor.impl.getLockoutModeForUser(userId);
@@ -243,7 +267,8 @@
     private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
             boolean credentialRequested, List<BiometricSensor> eligibleSensors,
             List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
-            boolean confirmationRequested, boolean ignoreEnrollmentState) {
+            boolean confirmationRequested, boolean ignoreEnrollmentState, int userId,
+            Context context) {
         mBiometricRequested = biometricRequested;
         mBiometricStrengthRequested = biometricStrengthRequested;
         this.credentialRequested = credentialRequested;
@@ -253,6 +278,8 @@
         this.credentialAvailable = credentialAvailable;
         this.confirmationRequested = confirmationRequested;
         this.ignoreEnrollmentState = ignoreEnrollmentState;
+        this.userId = userId;
+        this.context = context;
     }
 
     private Pair<BiometricSensor, Integer> calculateErrorByPriority() {
@@ -280,15 +307,35 @@
     private Pair<Integer, Integer> getInternalStatus() {
         @AuthenticatorStatus final int status;
         @BiometricAuthenticator.Modality int modality = TYPE_NONE;
+
+        final SensorPrivacyManager sensorPrivacyManager = context
+                .getSystemService(SensorPrivacyManager.class);
+
+        boolean cameraPrivacyEnabled = false;
+        if (sensorPrivacyManager != null) {
+            cameraPrivacyEnabled = sensorPrivacyManager
+                    .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId);
+        }
+
         if (mBiometricRequested && credentialRequested) {
             if (credentialAvailable || !eligibleSensors.isEmpty()) {
-                status = AUTHENTICATOR_OK;
-                if (credentialAvailable) {
-                    modality |= TYPE_CREDENTIAL;
-                }
                 for (BiometricSensor sensor : eligibleSensors) {
                     modality |= sensor.modality;
                 }
+
+                if (credentialAvailable) {
+                    modality |= TYPE_CREDENTIAL;
+                    status = AUTHENTICATOR_OK;
+                } else if (modality == TYPE_FACE && cameraPrivacyEnabled) {
+                    // If the only modality requested is face, credential is unavailable,
+                    // and the face sensor privacy is enabled then return
+                    // BIOMETRIC_SENSOR_PRIVACY_ENABLED.
+                    //
+                    // Note: This sensor will still be eligible for calls to authenticate.
+                    status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
+                } else {
+                    status = AUTHENTICATOR_OK;
+                }
             } else {
                 // Pick the first sensor error if it exists
                 if (!ineligibleSensors.isEmpty()) {
@@ -302,10 +349,18 @@
             }
         } else if (mBiometricRequested) {
             if (!eligibleSensors.isEmpty()) {
-                 status = AUTHENTICATOR_OK;
-                 for (BiometricSensor sensor : eligibleSensors) {
-                     modality |= sensor.modality;
-                 }
+                for (BiometricSensor sensor : eligibleSensors) {
+                    modality |= sensor.modality;
+                }
+                if (modality == TYPE_FACE && cameraPrivacyEnabled) {
+                    // If the only modality requested is face and the privacy is enabled
+                    // then return BIOMETRIC_SENSOR_PRIVACY_ENABLED.
+                    //
+                    // Note: This sensor will still be eligible for calls to authenticate.
+                    status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
+                } else {
+                    status = AUTHENTICATOR_OK;
+                }
             } else {
                 // Pick the first sensor error if it exists
                 if (!ineligibleSensors.isEmpty()) {
@@ -326,9 +381,9 @@
             Slog.e(TAG, "No authenticators requested");
             status = BIOMETRIC_NO_HARDWARE;
         }
-
         Slog.d(TAG, "getCanAuthenticateInternal Modality: " + modality
                 + " AuthenticatorStatus: " + status);
+
         return new Pair<>(modality, status);
     }
 
@@ -362,6 +417,7 @@
             case CREDENTIAL_NOT_ENROLLED:
             case BIOMETRIC_LOCKOUT_TIMED:
             case BIOMETRIC_LOCKOUT_PERMANENT:
+            case BIOMETRIC_SENSOR_PRIVACY_ENABLED:
                 break;
 
             case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 4f7c6b0..0e2582c 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -33,6 +33,7 @@
 import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENABLED_FOR_APPS;
 import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENROLLED;
 import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NO_HARDWARE;
+import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_SENSOR_PRIVACY_ENABLED;
 import static com.android.server.biometrics.PreAuthInfo.CREDENTIAL_NOT_ENROLLED;
 
 import android.annotation.NonNull;
@@ -278,6 +279,9 @@
             case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
                 biometricManagerCode = BiometricManager.BIOMETRIC_SUCCESS;
                 break;
+            case BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED:
+                biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+                break;
             default:
                 Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
                 biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
@@ -337,7 +341,8 @@
 
             case BIOMETRIC_LOCKOUT_PERMANENT:
                 return BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
-
+            case BIOMETRIC_SENSOR_PRIVACY_ENABLED:
+                return BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED;
             case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
             case BIOMETRIC_HARDWARE_NOT_DETECTED:
             case BIOMETRIC_NOT_ENABLED_FOR_APPS:
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 7341e74..358263d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -503,10 +503,14 @@
     protected int getShowOverlayReason() {
         if (isKeyguard()) {
             return BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
-        } else if (isSettings()) {
-            return BiometricOverlayConstants.REASON_AUTH_SETTINGS;
         } else if (isBiometricPrompt()) {
+            // BP reason always takes precedent over settings, since callers from within
+            // settings can always invoke BP.
             return BiometricOverlayConstants.REASON_AUTH_BP;
+        } else if (isSettings()) {
+            // This is pretty much only for FingerprintManager#authenticate usage from
+            // FingerprintSettings.
+            return BiometricOverlayConstants.REASON_AUTH_SETTINGS;
         } else {
             return BiometricOverlayConstants.REASON_AUTH_OTHER;
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 97d791b..4131ae1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -21,6 +21,7 @@
 import android.app.NotificationManager;
 import android.content.Context;
 import android.content.res.Resources;
+import android.hardware.SensorPrivacyManager;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricFaceConstants;
@@ -56,6 +57,7 @@
     @NonNull private final LockoutCache mLockoutCache;
     @Nullable private final NotificationManager mNotificationManager;
     @Nullable private ICancellationSignal mCancellationSignal;
+    @Nullable private SensorPrivacyManager mSensorPrivacyManager;
 
     private final int[] mBiometricPromptIgnoreList;
     private final int[] mBiometricPromptIgnoreListVendor;
@@ -81,6 +83,7 @@
         mUsageStats = usageStats;
         mLockoutCache = lockoutCache;
         mNotificationManager = context.getSystemService(NotificationManager.class);
+        mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
 
         final Resources resources = getContext().getResources();
         mBiometricPromptIgnoreList = resources.getIntArray(
@@ -108,7 +111,16 @@
     @Override
     protected void startHalOperation() {
         try {
-            mCancellationSignal = getFreshDaemon().authenticate(mOperationId);
+            if (mSensorPrivacyManager != null
+                    && mSensorPrivacyManager
+                    .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA,
+                    getTargetUserId())) {
+                onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+                        0 /* vendorCode */);
+                mCallback.onClientFinished(this, false /* success */);
+            } else {
+                mCancellationSignal = getFreshDaemon().authenticate(mOperationId);
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception when requesting auth", e);
             onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 2ef0911..2158dfe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -19,6 +19,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.hardware.SensorPrivacyManager;
+import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.common.ICancellationSignal;
 import android.hardware.biometrics.face.ISession;
@@ -41,6 +43,7 @@
 
     private final boolean mIsStrongBiometric;
     @Nullable private ICancellationSignal mCancellationSignal;
+    @Nullable private SensorPrivacyManager mSensorPrivacyManager;
 
     public FaceDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
             @NonNull IBinder token, long requestId,
@@ -51,6 +54,7 @@
                 BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
         setRequestId(requestId);
         mIsStrongBiometric = isStrongBiometric;
+        mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
     }
 
     @Override
@@ -73,6 +77,14 @@
 
     @Override
     protected void startHalOperation() {
+        if (mSensorPrivacyManager != null
+                && mSensorPrivacyManager
+                .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, getTargetUserId())) {
+            onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+            mCallback.onClientFinished(this, false /* success */);
+            return;
+        }
+
         try {
             mCancellationSignal = getFreshDaemon().detectInteraction();
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 40f2801..7548d28 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
+import android.hardware.SensorPrivacyManager;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricFaceConstants;
@@ -55,6 +56,7 @@
     private final int[] mKeyguardIgnoreListVendor;
 
     private int mLastAcquire;
+    private SensorPrivacyManager mSensorPrivacyManager;
 
     FaceAuthenticationClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
@@ -71,6 +73,7 @@
                 isKeyguardBypassEnabled);
         setRequestId(requestId);
         mUsageStats = usageStats;
+        mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
 
         final Resources resources = getContext().getResources();
         mBiometricPromptIgnoreList = resources.getIntArray(
@@ -97,6 +100,15 @@
 
     @Override
     protected void startHalOperation() {
+
+        if (mSensorPrivacyManager != null
+                && mSensorPrivacyManager
+                .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, getTargetUserId())) {
+            onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+            mCallback.onClientFinished(this, false /* success */);
+            return;
+        }
+
         try {
             getFreshDaemon().authenticate(mOperationId);
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 5ce72c2..3120dc5 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -25,7 +25,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.compat.CompatChanges;
-import android.app.TaskStackListener;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.Disabled;
 import android.compat.annotation.Overridable;
@@ -35,6 +34,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
 import android.content.res.Configuration;
 import android.hardware.CameraSessionStats;
 import android.hardware.CameraStreamStats;
@@ -84,7 +84,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
@@ -309,8 +308,6 @@
 
     private final DisplayWindowListener mDisplayWindowListener = new DisplayWindowListener();
 
-    private final TaskStateHandler mTaskStackListener = new TaskStateHandler();
-
     public static final class TaskInfo {
         public int frontTaskId;
         public boolean isResizeable;
@@ -320,54 +317,6 @@
         public int userId;
     }
 
-    private final class TaskStateHandler extends TaskStackListener {
-        private final Object mMapLock = new Object();
-
-        // maps the package name to its corresponding current top level task id
-        @GuardedBy("mMapLock")
-        private final ArrayMap<String, TaskInfo> mTaskInfoMap = new ArrayMap<>();
-
-        @Override
-        public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
-            synchronized (mMapLock) {
-                TaskInfo info = new TaskInfo();
-                info.frontTaskId = taskInfo.taskId;
-                info.isResizeable =
-                        (taskInfo.topActivityInfo.resizeMode != RESIZE_MODE_UNRESIZEABLE);
-                info.displayId = taskInfo.displayId;
-                info.userId = taskInfo.userId;
-                info.isFixedOrientationLandscape = ActivityInfo.isFixedOrientationLandscape(
-                        taskInfo.topActivityInfo.screenOrientation);
-                info.isFixedOrientationPortrait = ActivityInfo.isFixedOrientationPortrait(
-                        taskInfo.topActivityInfo.screenOrientation);
-                mTaskInfoMap.put(taskInfo.topActivityInfo.packageName, info);
-            }
-        }
-
-        @Override
-        public void onTaskRemoved(int taskId) {
-            synchronized (mMapLock) {
-                for (Map.Entry<String, TaskInfo> entry : mTaskInfoMap.entrySet()){
-                    if (entry.getValue().frontTaskId == taskId) {
-                        mTaskInfoMap.remove(entry.getKey());
-                        break;
-                    }
-                }
-            }
-        }
-
-        public @Nullable TaskInfo getFrontTaskInfo(String packageName) {
-            synchronized (mMapLock) {
-                if (mTaskInfoMap.containsKey(packageName)) {
-                    return mTaskInfoMap.get(packageName);
-                }
-            }
-
-            Log.e(TAG, "Top task with package name: " + packageName + " not found!");
-            return null;
-        }
-    }
-
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -490,18 +439,53 @@
 
     private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
         @Override
-        public int getRotateAndCropOverride(String packageName, int lensFacing) {
+        public int getRotateAndCropOverride(String packageName, int lensFacing, int userId) {
             if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
                 Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
                         " camera service UID!");
                 return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
             }
 
+            TaskInfo taskInfo = null;
+            ParceledListSlice<ActivityManager.RecentTaskInfo> recentTasks = null;
+
+            try {
+                recentTasks = ActivityTaskManager.getService().getRecentTasks(/*maxNum*/1,
+                        /*flags*/ 0, userId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to query recent tasks!");
+                return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+            }
+
+            if ((recentTasks != null) && (!recentTasks.getList().isEmpty())) {
+                ActivityManager.RecentTaskInfo task = recentTasks.getList().get(0);
+                if (packageName.equals(task.topActivityInfo.packageName)) {
+                    taskInfo = new TaskInfo();
+                    taskInfo.frontTaskId = task.taskId;
+                    taskInfo.isResizeable =
+                            (task.topActivityInfo.resizeMode != RESIZE_MODE_UNRESIZEABLE);
+                    taskInfo.displayId = task.displayId;
+                    taskInfo.userId = task.userId;
+                    taskInfo.isFixedOrientationLandscape =
+                            ActivityInfo.isFixedOrientationLandscape(
+                                    task.topActivityInfo.screenOrientation);
+                    taskInfo.isFixedOrientationPortrait =
+                            ActivityInfo.isFixedOrientationPortrait(
+                                    task.topActivityInfo.screenOrientation);
+                } else {
+                    Log.e(TAG, "Recent task package name: " + task.topActivityInfo.packageName
+                            + " doesn't match with camera client package name: " + packageName);
+                    return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+                }
+            } else {
+                Log.e(TAG, "Recent task list is empty!");
+                return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+            }
+
             // TODO: Modify the sensor orientation in camera characteristics along with any 3A
             //  regions in capture requests/results to account for thea physical rotation. The
             //  former is somewhat tricky as it assumes that camera clients always check for the
             //  current value by retrieving the camera characteristics from the camera device.
-            TaskInfo taskInfo = mTaskStackListener.getFrontTaskInfo(packageName);
             if ((taskInfo != null) && (CompatChanges.isChangeEnabled(
                         OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS, packageName,
                         UserHandle.getUserHandleForUid(taskInfo.userId)))) {
@@ -673,12 +657,6 @@
             CameraStatsJobService.schedule(mContext);
 
             try {
-                ActivityTaskManager.getService().registerTaskStackListener(mTaskStackListener);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to register task stack listener!");
-            }
-
-            try {
                 int[] displayIds = WindowManagerGlobal.getWindowManagerService()
                         .registerDisplayWindowListener(mDisplayWindowListener);
                 for (int i = 0; i < displayIds.length; i++) {
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 155b618..737b653 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -177,7 +177,7 @@
     protected interface LocationTransport {
 
         void deliverOnLocationChanged(LocationResult locationResult,
-                @Nullable Runnable onCompleteCallback) throws Exception;
+                @Nullable IRemoteCallback onCompleteCallback) throws Exception;
         void deliverOnFlushComplete(int requestCode) throws Exception;
     }
 
@@ -197,9 +197,8 @@
 
         @Override
         public void deliverOnLocationChanged(LocationResult locationResult,
-                @Nullable Runnable onCompleteCallback) throws RemoteException {
-            mListener.onLocationChanged(locationResult.asList(),
-                    SingleUseCallback.wrap(onCompleteCallback));
+                @Nullable IRemoteCallback onCompleteCallback) throws RemoteException {
+            mListener.onLocationChanged(locationResult.asList(), onCompleteCallback);
         }
 
         @Override
@@ -227,7 +226,7 @@
 
         @Override
         public void deliverOnLocationChanged(LocationResult locationResult,
-                @Nullable Runnable onCompleteCallback)
+                @Nullable IRemoteCallback onCompleteCallback)
                 throws PendingIntent.CanceledException {
             BroadcastOptions options = BroadcastOptions.makeBasic();
             options.setDontSendToRestrictedApps(true);
@@ -243,20 +242,34 @@
                 intent.putExtra(KEY_LOCATIONS, locationResult.asList().toArray(new Location[0]));
             }
 
+            PendingIntent.OnFinished onFinished = null;
+
             // send() SHOULD only run the completion callback if it completes successfully. however,
-            // b/199464864 (which could not be fixed in the S timeframe) means that it's possible
+            // b/201299281 (which could not be fixed in the S timeframe) means that it's possible
             // for send() to throw an exception AND run the completion callback. if this happens, we
             // would over-release the wakelock... we take matters into our own hands to ensure that
             // the completion callback can only be run if send() completes successfully. this means
             // the completion callback may be run inline - but as we've never specified what thread
             // the callback is run on, this is fine.
-            GatedCallback gatedCallback = new GatedCallback(onCompleteCallback);
+            GatedCallback gatedCallback;
+            if (onCompleteCallback != null) {
+                gatedCallback = new GatedCallback(() -> {
+                    try {
+                        onCompleteCallback.sendResult(null);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                });
+                onFinished = (pI, i, rC, rD, rE) -> gatedCallback.run();
+            } else {
+                gatedCallback = new GatedCallback(null);
+            }
 
             mPendingIntent.send(
                     mContext,
                     0,
                     intent,
-                    (pI, i, rC, rD, rE) -> gatedCallback.run(),
+                    onFinished,
                     null,
                     null,
                     options.toBundle());
@@ -293,7 +306,7 @@
 
         @Override
         public void deliverOnLocationChanged(@Nullable LocationResult locationResult,
-                @Nullable Runnable onCompleteCallback)
+                @Nullable IRemoteCallback onCompleteCallback)
                 throws RemoteException {
             // ILocationCallback doesn't currently support completion callbacks
             Preconditions.checkState(onCompleteCallback == null);
@@ -714,6 +727,13 @@
 
         final PowerManager.WakeLock mWakeLock;
 
+        // b/206340085 - if we allocate a new wakelock releaser object for every delivery we
+        // increase the risk of resource starvation. if a client stops processing deliveries the
+        // system server binder allocation pool will be starved as we continue to queue up
+        // deliveries, each with a new allocation. in order to mitigate this, we use a single
+        // releaser object per registration rather than per delivery.
+        final ExternalWakeLockReleaser mWakeLockReleaser;
+
         private volatile ProviderTransport mProviderTransport;
         private int mNumLocationsDelivered = 0;
         private long mExpirationRealtimeMs = Long.MAX_VALUE;
@@ -727,6 +747,7 @@
                     .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
             mWakeLock.setReferenceCounted(true);
             mWakeLock.setWorkSource(request.getWorkSource());
+            mWakeLockReleaser = new ExternalWakeLockReleaser(identity, mWakeLock);
         }
 
         @Override
@@ -943,7 +964,7 @@
                     }
 
                     listener.deliverOnLocationChanged(deliverLocationResult,
-                            mUseWakeLock ? mWakeLock::release : null);
+                            mUseWakeLock ? mWakeLockReleaser : null);
                     EVENT_LOG.logProviderDeliveredLocations(mName, locationResult.size(),
                             getIdentity());
                 }
@@ -2761,7 +2782,7 @@
         @GuardedBy("this")
         private boolean mRun;
 
-        GatedCallback(Runnable callback) {
+        GatedCallback(@Nullable Runnable callback) {
             mCallback = callback;
         }
 
@@ -2796,4 +2817,24 @@
             }
         }
     }
+
+    private static class ExternalWakeLockReleaser extends IRemoteCallback.Stub {
+
+        private final CallerIdentity mIdentity;
+        private final PowerManager.WakeLock mWakeLock;
+
+        ExternalWakeLockReleaser(CallerIdentity identity, PowerManager.WakeLock wakeLock) {
+            mIdentity = identity;
+            mWakeLock = Objects.requireNonNull(wakeLock);
+        }
+
+        @Override
+        public void sendResult(Bundle data) {
+            try {
+                mWakeLock.release();
+            } catch (RuntimeException e) {
+                Log.e(TAG, "wakelock over-released by " + mIdentity, e);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index 22a675a..5e38bca 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -23,6 +23,8 @@
 import static com.android.server.location.LocationManagerService.TAG;
 import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG;
 
+import static java.lang.Math.max;
+
 import android.annotation.Nullable;
 import android.location.Location;
 import android.location.LocationResult;
@@ -53,6 +55,7 @@
         implements DeviceIdleHelper.DeviceIdleListener, DeviceIdleInternal.StationaryListener {
 
     private static final long MAX_STATIONARY_LOCATION_AGE_MS = 30000;
+    private static final long MIN_INTERVAL_MS = 1000;
 
     final Object mLock = new Object();
 
@@ -179,7 +182,7 @@
                 && mLastLocation != null
                 && mLastLocation.getElapsedRealtimeAgeMillis(mDeviceStationaryRealtimeMs)
                 <= MAX_STATIONARY_LOCATION_AGE_MS) {
-            throttlingIntervalMs = mIncomingRequest.getIntervalMillis();
+            throttlingIntervalMs = max(mIncomingRequest.getIntervalMillis(), MIN_INTERVAL_MS);
         }
 
         ProviderRequest newRequest;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 20687c6..cfefffc 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -4899,6 +4899,8 @@
                 ? ALLOWED_REASON_POWER_SAVE_ALLOWLIST : 0);
         newAllowedReasons |= (isWhitelistedFromPowerSaveExceptIdleUL(uid)
                 ? ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST : 0);
+        newAllowedReasons |= (uidBlockedState.allowedReasons
+                & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS);
 
         if (LOGV) {
             Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7ed897d..7455cec 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -266,7 +266,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.SomeArgs;
@@ -615,12 +614,6 @@
     private NotificationRecordLogger mNotificationRecordLogger;
     private InstanceIdSequence mNotificationInstanceIdSequence;
     private Set<String> mMsgPkgsAllowedAsConvos = new HashSet();
-    protected static final String ACTION_ENABLE_NAS =
-            "android.server.notification.action.ENABLE_NAS";
-    protected static final String ACTION_DISABLE_NAS =
-            "android.server.notification.action.DISABLE_NAS";
-    protected static final String ACTION_LEARNMORE_NAS =
-            "android.server.notification.action.LEARNMORE_NAS";
 
     static class Archive {
         final SparseArray<Boolean> mEnabled;
@@ -755,97 +748,27 @@
         setDefaultAssistantForUser(userId);
     }
 
-    protected void migrateDefaultNASShowNotificationIfNecessary() {
+    protected void migrateDefaultNAS() {
         final List<UserInfo> activeUsers = mUm.getUsers();
         for (UserInfo userInfo : activeUsers) {
             int userId = userInfo.getUserHandle().getIdentifier();
             if (isNASMigrationDone(userId) || mUm.isManagedProfile(userId)) {
                 continue;
             }
-            if (mAssistants.hasUserSet(userId)) {
-                ComponentName defaultFromConfig = mAssistants.getDefaultFromConfig();
-                List<ComponentName> allowedComponents = mAssistants.getAllowedComponents(userId);
-                if (allowedComponents.size() == 0) {
-                    setNASMigrationDone(userId);
-                    mAssistants.clearDefaults();
-                    continue;
-                } else if (allowedComponents.contains(defaultFromConfig)) {
-                    setNASMigrationDone(userId);
-                    mAssistants.resetDefaultFromConfig();
-                    continue;
-                }
-                // TODO(b/192450820): re-enable when "user set" isn't over triggering
-                //User selected different NAS, need onboarding
-                /*enqueueNotificationInternal(getContext().getPackageName(),
-                        getContext().getOpPackageName(), Binder.getCallingUid(),
-                        Binder.getCallingPid(), TAG,
-                        SystemMessageProto.SystemMessage.NOTE_NAS_UPGRADE,
-                        createNASUpgradeNotification(userId), userId);*/
+            List<ComponentName> allowedComponents = mAssistants.getAllowedComponents(userId);
+            if (allowedComponents.size() == 0) { // user set to none
+                Slog.d(TAG, "NAS Migration: user set to none, disable new NAS setting");
+                setNASMigrationDone(userId);
+                mAssistants.clearDefaults();
+            } else {
+                Slog.d(TAG, "Reset NAS setting and migrate to new default");
+                resetAssistantUserSet(userId);
+                // migrate to new default and set migration done
+                mAssistants.resetDefaultAssistantsIfNecessary();
             }
         }
     }
 
-    protected Notification createNASUpgradeNotification(int userId) {
-        final Bundle extras = new Bundle();
-        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
-                getContext().getResources().getString(R.string.global_action_settings));
-        int title = R.string.nas_upgrade_notification_title;
-        int content = R.string.nas_upgrade_notification_content;
-
-        Intent onboardingIntent = new Intent(Settings.ACTION_NOTIFICATION_ASSISTANT_SETTINGS);
-        onboardingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-
-        Intent enableIntent = new Intent(ACTION_ENABLE_NAS);
-        enableIntent.putExtra(Intent.EXTRA_USER_ID, userId);
-        PendingIntent enableNASPendingIntent = PendingIntent.getBroadcast(getContext(),
-                0, enableIntent, PendingIntent.FLAG_IMMUTABLE);
-
-        Intent disableIntent = new Intent(ACTION_DISABLE_NAS);
-        disableIntent.putExtra(Intent.EXTRA_USER_ID, userId);
-        PendingIntent disableNASPendingIntent = PendingIntent.getBroadcast(getContext(),
-                0, disableIntent, PendingIntent.FLAG_IMMUTABLE);
-
-        Intent learnMoreIntent = new Intent(ACTION_LEARNMORE_NAS);
-        learnMoreIntent.putExtra(Intent.EXTRA_USER_ID, userId);
-        PendingIntent learnNASPendingIntent = PendingIntent.getBroadcast(getContext(),
-                0, learnMoreIntent, PendingIntent.FLAG_IMMUTABLE);
-
-        Notification.Action enableNASAction = new Notification.Action.Builder(
-                0,
-                getContext().getResources().getString(
-                        R.string.nas_upgrade_notification_enable_action),
-                enableNASPendingIntent).build();
-
-        Notification.Action disableNASAction = new Notification.Action.Builder(
-                0,
-                getContext().getResources().getString(
-                        R.string.nas_upgrade_notification_disable_action),
-                disableNASPendingIntent).build();
-
-        Notification.Action learnMoreNASAction = new Notification.Action.Builder(
-                0,
-                getContext().getResources().getString(
-                        R.string.nas_upgrade_notification_learn_more_action),
-                learnNASPendingIntent).build();
-
-
-        return new Notification.Builder(getContext(), SystemNotificationChannels.SYSTEM_CHANGES)
-                .setAutoCancel(false)
-                .setOngoing(true)
-                .setTicker(getContext().getResources().getString(title))
-                .setSmallIcon(R.drawable.ic_settings_24dp)
-                .setContentTitle(getContext().getResources().getString(title))
-                .setContentText(getContext().getResources().getString(content))
-                .setContentIntent(PendingIntent.getActivity(getContext(), 0, onboardingIntent,
-                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
-                .setLocalOnly(true)
-                .setStyle(new Notification.BigTextStyle())
-                .addAction(enableNASAction)
-                .addAction(disableNASAction)
-                .addAction(learnMoreNASAction)
-                .build();
-    }
-
     @VisibleForTesting
     void setNASMigrationDone(int baseUserId) {
         for (int profileId : mUm.getProfileIds(baseUserId, false)) {
@@ -1861,41 +1784,6 @@
         }
     };
 
-    private final BroadcastReceiver mNASIntentReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1);
-            if (ACTION_ENABLE_NAS.equals(action)) {
-                mAssistants.resetDefaultFromConfig();
-                setNotificationAssistantAccessGrantedForUserInternal(
-                        CollectionUtils.firstOrNull(mAssistants.getDefaultComponents()),
-                        userId, true, true);
-                setNASMigrationDone(userId);
-                cancelNotificationInternal(getContext().getPackageName(),
-                        getContext().getOpPackageName(), Binder.getCallingUid(),
-                        Binder.getCallingPid(), TAG,
-                        SystemMessageProto.SystemMessage.NOTE_NAS_UPGRADE, userId);
-            } else if (ACTION_DISABLE_NAS.equals(action)) {
-                //Set default NAS to be null if user selected none during migration
-                mAssistants.clearDefaults();
-                setNotificationAssistantAccessGrantedForUserInternal(
-                        null, userId, true, true);
-                setNASMigrationDone(userId);
-                cancelNotificationInternal(getContext().getPackageName(),
-                        getContext().getOpPackageName(), Binder.getCallingUid(),
-                        Binder.getCallingPid(), TAG,
-                        SystemMessageProto.SystemMessage.NOTE_NAS_UPGRADE, userId);
-            } else if (ACTION_LEARNMORE_NAS.equals(action)) {
-                Intent i = new Intent(getContext(), NASLearnMoreActivity.class);
-                i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                getContext().sendBroadcastAsUser(
-                        new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.of(userId));
-                getContext().startActivity(i);
-            }
-        }
-    };
-
     private final class SettingsObserver extends ContentObserver {
         private final Uri NOTIFICATION_BADGING_URI
                 = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
@@ -2407,12 +2295,6 @@
 
         IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
         getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
-
-        IntentFilter nasFilter = new IntentFilter();
-        nasFilter.addAction(ACTION_ENABLE_NAS);
-        nasFilter.addAction(ACTION_DISABLE_NAS);
-        nasFilter.addAction(ACTION_LEARNMORE_NAS);
-        getContext().registerReceiver(mNASIntentReceiver, nasFilter);
     }
 
     /**
@@ -2424,7 +2306,6 @@
         getContext().unregisterReceiver(mNotificationTimeoutReceiver);
         getContext().unregisterReceiver(mRestoreReceiver);
         getContext().unregisterReceiver(mLocaleChangeReceiver);
-        getContext().unregisterReceiver(mNASIntentReceiver);
 
         if (mDeviceConfigChangedListener != null) {
             DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
@@ -2691,7 +2572,7 @@
             mConditionProviders.onBootPhaseAppsCanStart();
             mHistoryManager.onBootPhaseAppsCanStart();
             registerDeviceConfigChange();
-            migrateDefaultNASShowNotificationIfNecessary();
+            migrateDefaultNAS();
         } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
             mSnoozeHelper.scheduleRepostsForPersistedNotifications(System.currentTimeMillis());
         }
@@ -5229,10 +5110,6 @@
         public void setNASMigrationDoneAndResetDefault(int userId, boolean loadFromConfig) {
             checkCallerIsSystem();
             setNASMigrationDone(userId);
-            cancelNotificationInternal(getContext().getPackageName(),
-                    getContext().getOpPackageName(), Binder.getCallingUid(),
-                    Binder.getCallingPid(), TAG,
-                    SystemMessageProto.SystemMessage.NOTE_NAS_UPGRADE, userId);
             if (loadFromConfig) {
                 mAssistants.resetDefaultFromConfig();
             } else {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 7d5125f..393d101 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -160,6 +160,7 @@
 import static com.android.server.wm.ActivityRecordProto.NUM_INTERESTING_WINDOWS;
 import static com.android.server.wm.ActivityRecordProto.PIP_AUTO_ENTER_ENABLED;
 import static com.android.server.wm.ActivityRecordProto.PROC_ID;
+import static com.android.server.wm.ActivityRecordProto.PROVIDES_MAX_BOUNDS;
 import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN;
 import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE;
 import static com.android.server.wm.ActivityRecordProto.STARTING_DISPLAYED;
@@ -261,6 +262,7 @@
 import android.content.LocusId;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ConstrainDisplayApisConfig;
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -596,6 +598,8 @@
      */
     private CompatDisplayInsets mCompatDisplayInsets;
 
+    private static ConstrainDisplayApisConfig sConstrainDisplayApisConfig;
+
     boolean pendingVoiceInteractionStart;   // Waiting for activity-invoked voice session
     IVoiceInteractionSession voiceSession;  // Voice interaction session for this activity
 
@@ -1162,8 +1166,10 @@
             if (info.configChanges != 0) {
                 pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
             }
-            pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis());
-            pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis());
+            pw.println(prefix + "neverSandboxDisplayApis=" + info.neverSandboxDisplayApis(
+                    sConstrainDisplayApisConfig));
+            pw.println(prefix + "alwaysSandboxDisplayApis=" + info.alwaysSandboxDisplayApis(
+                    sConstrainDisplayApisConfig));
         }
         if (mLastParentBeforePip != null) {
             pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId);
@@ -1769,6 +1775,10 @@
             info.windowLayout.windowLayoutAffinity =
                     uid + ":" + info.windowLayout.windowLayoutAffinity;
         }
+        // Initialize once, when we know all system services are available.
+        if (sConstrainDisplayApisConfig == null) {
+            sConstrainDisplayApisConfig = new ConstrainDisplayApisConfig();
+        }
         stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
         nonLocalizedLabel = aInfo.nonLocalizedLabel;
         labelRes = aInfo.labelRes;
@@ -6565,6 +6575,13 @@
             } else if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_ICON) {
                 return false;
             }
+            // Choose the default behavior for Launcher and SystemUI when the SplashScreen style is
+            // not specified in the ActivityOptions.
+            if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME) {
+                return false;
+            } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
+                return true;
+            }
         }
         if (sourceRecord == null) {
             sourceRecord = searchCandidateLaunchingActivity();
@@ -6574,14 +6591,11 @@
             return sourceRecord.mSplashScreenStyleEmpty;
         }
 
-        // If this activity was launched from a system surface, never use an empty splash screen
+        // If this activity was launched from Launcher or System for first start, never use an
+        // empty splash screen.
         // Need to check sourceRecord before in case this activity is launched from service.
-        if (launchedFromSystemSurface()) {
-            return false;
-        }
-
-        // Otherwise use empty.
-        return true;
+        return !startActivity || !(mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEM
+                || mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME);
     }
 
     private int getSplashscreenTheme() {
@@ -7461,8 +7475,8 @@
                                 + "should create compatDisplayInsets = %s",
                         getUid(),
                         mTmpBounds,
-                        info.neverSandboxDisplayApis(),
-                        info.alwaysSandboxDisplayApis(),
+                        info.neverSandboxDisplayApis(sConstrainDisplayApisConfig),
+                        info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig),
                         !matchParentBounds(),
                         mCompatDisplayInsets != null,
                         shouldCreateCompatDisplayInsets());
@@ -8023,11 +8037,11 @@
             return false;
         }
         // Never apply sandboxing to an app that should be explicitly excluded from the config.
-        if (info != null && info.neverSandboxDisplayApis()) {
+        if (info.neverSandboxDisplayApis(sConstrainDisplayApisConfig)) {
             return false;
         }
         // Always apply sandboxing to an app that should be explicitly included from the config.
-        if (info != null && info.alwaysSandboxDisplayApis()) {
+        if (info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig)) {
             return true;
         }
         // Max bounds should be sandboxed when an activity should have compatDisplayInsets, and it
@@ -9043,6 +9057,9 @@
         proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled());
         proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
         proto.write(MIN_ASPECT_RATIO, getMinAspectRatio());
+        // Only record if max bounds sandboxing is applied, if the caller has the necessary
+        // permission to access the device configs.
+        proto.write(PROVIDES_MAX_BOUNDS, providesMaxBounds());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 2cf23c5..436a325 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -72,6 +72,8 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
 import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
@@ -1275,7 +1277,7 @@
 
         // This is used to block background activity launch even if the app is still
         // visible to user after user clicking home button.
-        final boolean appSwitchAllowed = mService.getBalAppSwitchesAllowed();
+        final int appSwitchState = mService.getBalAppSwitchesState();
 
         // don't abort if the callingUid has a visible window or is a persistent system process
         final int callingUidProcState = mService.mActiveUids.getUidState(callingUid);
@@ -1288,7 +1290,9 @@
 
         // Normal apps with visible app window will be allowed to start activity if app switching
         // is allowed, or apps like live wallpaper with non app visible window will be allowed.
-        if (((appSwitchAllowed || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
+        final boolean appSwitchAllowedOrFg =
+                appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
+        if (((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
                 && callingUidHasAnyVisibleWindow)
                 || isCallingUidPersistentSystemProcess) {
             if (DEBUG_ACTIVITY_STARTS) {
@@ -1398,7 +1402,7 @@
         // don't abort if the callerApp or other processes of that uid are allowed in any way
         if (callerApp != null) {
             // first check the original calling process
-            if (callerApp.areBackgroundActivityStartsAllowed(appSwitchAllowed)) {
+            if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) {
                 if (DEBUG_ACTIVITY_STARTS) {
                     Slog.d(TAG, "Background activity start allowed: callerApp process (pid = "
                             + callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed");
@@ -1412,7 +1416,7 @@
                 for (int i = uidProcesses.size() - 1; i >= 0; i--) {
                     final WindowProcessController proc = uidProcesses.valueAt(i);
                     if (proc != callerApp
-                            && proc.areBackgroundActivityStartsAllowed(appSwitchAllowed)) {
+                            && proc.areBackgroundActivityStartsAllowed(appSwitchState)) {
                         if (DEBUG_ACTIVITY_STARTS) {
                             Slog.d(TAG,
                                     "Background activity start allowed: process " + proc.getPid()
@@ -1426,7 +1430,7 @@
         // anything that has fallen through would currently be aborted
         Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
                 + "; callingUid: " + callingUid
-                + "; appSwitchAllowed: " + appSwitchAllowed
+                + "; appSwitchState: " + appSwitchState
                 + "; isCallingUidForeground: " + isCallingUidForeground
                 + "; callingUidHasAnyVisibleWindow: " + callingUidHasAnyVisibleWindow
                 + "; callingUidProcState: " + DebugUtils.valueToString(ActivityManager.class,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 60a514e..c8227d9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -504,7 +504,27 @@
      * Whether normal application switches are allowed; a call to {@link #stopAppSwitches()
      * disables this.
      */
-    private volatile boolean mAppSwitchesAllowed = true;
+    private volatile int mAppSwitchesState = APP_SWITCH_ALLOW;
+
+    // The duration of resuming foreground app switch from disallow.
+    private static final long RESUME_FG_APP_SWITCH_MS = 500;
+
+    /** App switch is not allowed. */
+    static final int APP_SWITCH_DISALLOW = 0;
+
+    /** App switch is allowed only if the activity launch was requested by a foreground app. */
+    static final int APP_SWITCH_FG_ONLY = 1;
+
+    /** App switch is allowed. */
+    static final int APP_SWITCH_ALLOW = 2;
+
+    @IntDef({
+            APP_SWITCH_DISALLOW,
+            APP_SWITCH_FG_ONLY,
+            APP_SWITCH_ALLOW,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface AppSwitchState {}
 
     /**
      * Last stop app switches time, apps finished before this time cannot start background activity
@@ -1247,7 +1267,7 @@
             if (topFocusedRootTask != null && topFocusedRootTask.getTopResumedActivity() != null
                     && topFocusedRootTask.getTopResumedActivity().info.applicationInfo.uid
                     == Binder.getCallingUid()) {
-                mAppSwitchesAllowed = true;
+                mAppSwitchesState = APP_SWITCH_ALLOW;
             }
         }
         return pir.sendInner(0, fillInIntent, resolvedType, allowlistToken, null, null,
@@ -2158,8 +2178,8 @@
     /**
      * Return true if app switching is allowed.
      */
-    boolean getBalAppSwitchesAllowed() {
-        return mAppSwitchesAllowed;
+    @AppSwitchState int getBalAppSwitchesState() {
+        return mAppSwitchesState;
     }
 
     /** Register an {@link AnrController} to control the ANR dialog behavior */
@@ -3680,8 +3700,10 @@
     public void stopAppSwitches() {
         mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "stopAppSwitches");
         synchronized (mGlobalLock) {
-            mAppSwitchesAllowed = false;
+            mAppSwitchesState = APP_SWITCH_DISALLOW;
             mLastStopAppSwitchesTime = SystemClock.uptimeMillis();
+            mH.removeMessages(H.RESUME_FG_APP_SWITCH_MSG);
+            mH.sendEmptyMessageDelayed(H.RESUME_FG_APP_SWITCH_MSG, RESUME_FG_APP_SWITCH_MS);
         }
     }
 
@@ -3689,7 +3711,8 @@
     public void resumeAppSwitches() {
         mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
         synchronized (mGlobalLock) {
-            mAppSwitchesAllowed = true;
+            mAppSwitchesState = APP_SWITCH_ALLOW;
+            mH.removeMessages(H.RESUME_FG_APP_SWITCH_MSG);
         }
     }
 
@@ -5170,6 +5193,7 @@
         static final int REPORT_TIME_TRACKER_MSG = 1;
         static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
         static final int END_POWER_MODE_UNKNOWN_VISIBILITY_MSG = 3;
+        static final int RESUME_FG_APP_SWITCH_MSG = 4;
 
         static final int FIRST_ACTIVITY_TASK_MSG = 100;
         static final int FIRST_SUPERVISOR_TASK_MSG = 200;
@@ -5207,6 +5231,14 @@
                     }
                 }
                 break;
+                case RESUME_FG_APP_SWITCH_MSG: {
+                    synchronized (mGlobalLock) {
+                        if (mAppSwitchesState == APP_SWITCH_DISALLOW) {
+                            mAppSwitchesState = APP_SWITCH_FG_ONLY;
+                        }
+                    }
+                }
+                break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index b9353e1..421a1c9 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1634,21 +1634,21 @@
     }
 
     @TransitionType int getKeyguardTransition() {
-        // In case we unocclude Keyguard and occlude it again, meaning that we never actually
-        // unoccclude/occlude Keyguard, but just run a normal transition.
-        final int occludeIndex = mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_UNOCCLUDE);
-        if (occludeIndex != -1
-                && occludeIndex < mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_OCCLUDE)) {
+        if (mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_GOING_AWAY) != -1) {
+            return TRANSIT_KEYGUARD_GOING_AWAY;
+        }
+        final int unoccludeIndex = mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_UNOCCLUDE);
+        final int occludeIndex = mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_OCCLUDE);
+        // No keyguard related transition requests.
+        if (unoccludeIndex == -1 && occludeIndex == -1) {
             return TRANSIT_NONE;
         }
-
-        for (int i = 0; i < mNextAppTransitionRequests.size(); ++i) {
-            final @TransitionType int transit = mNextAppTransitionRequests.get(i);
-            if (isKeyguardTransit(transit)) {
-                return transit;
-            }
+        // In case we unocclude Keyguard and occlude it again, meaning that we never actually
+        // unoccclude/occlude Keyguard, but just run a normal transition.
+        if (unoccludeIndex != -1 && unoccludeIndex < occludeIndex) {
+            return TRANSIT_NONE;
         }
-        return TRANSIT_NONE;
+        return unoccludeIndex != -1 ? TRANSIT_KEYGUARD_UNOCCLUDE : TRANSIT_KEYGUARD_OCCLUDE;
     }
 
     @TransitionType int getFirstAppTransition() {
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 71a10df..0afd872 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -20,6 +20,8 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -70,13 +72,13 @@
     }
 
     boolean areBackgroundActivityStartsAllowed(int pid, int uid, String packageName,
-            boolean appSwitchAllowed, boolean isCheckingForFgsStart,
+            int appSwitchState, boolean isCheckingForFgsStart,
             boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,
             long lastStopAppSwitchesTime, long lastActivityLaunchTime,
             long lastActivityFinishTime) {
         // If app switching is not allowed, we ignore all the start activity grace period
         // exception so apps cannot start itself in onPause() after pressing home button.
-        if (appSwitchAllowed) {
+        if (appSwitchState == APP_SWITCH_ALLOW) {
             // Allow if any activity in the caller has either started or finished very recently, and
             // it must be started or finished after last stop app switches time.
             final long now = SystemClock.uptimeMillis();
@@ -111,7 +113,8 @@
             return true;
         }
         // Allow if the caller has an activity in any foreground task.
-        if (appSwitchAllowed && hasActivityInVisibleTask) {
+        if (hasActivityInVisibleTask
+                && (appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY)) {
             if (DEBUG_ACTIVITY_STARTS) {
                 Slog.d(TAG, "[Process(" + pid
                         + ")] Activity start allowed: process has activity in foreground task");
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 35e9c8f..d1e9d6b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2039,16 +2039,14 @@
         final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout();
         final RoundedCorners roundedCorners = calculateRoundedCornersForRotation(rotation);
 
-        final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
-                displayCutout);
-        final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
+        final Point appSize = mDisplayPolicy.getNonDecorDisplaySize(dw, dh, rotation, uiMode,
                 displayCutout);
         mDisplayInfo.rotation = rotation;
         mDisplayInfo.logicalWidth = dw;
         mDisplayInfo.logicalHeight = dh;
         mDisplayInfo.logicalDensityDpi = mBaseDisplayDensity;
-        mDisplayInfo.appWidth = appWidth;
-        mDisplayInfo.appHeight = appHeight;
+        mDisplayInfo.appWidth = appSize.x;
+        mDisplayInfo.appHeight = appSize.y;
         if (isDefaultDisplay) {
             mDisplayInfo.getLogicalMetrics(mRealDisplayMetrics,
                     CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
@@ -2177,24 +2175,22 @@
     /** Compute configuration related to application without changing current display. */
     private void computeScreenAppConfiguration(Configuration outConfig, int dw, int dh,
             int rotation, int uiMode, DisplayCutout displayCutout) {
-        final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
-                displayCutout);
-        final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
+        final Point appSize = mDisplayPolicy.getNonDecorDisplaySize(dw, dh, rotation, uiMode,
                 displayCutout);
         mDisplayPolicy.getNonDecorInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
         final int leftInset = mTmpRect.left;
         final int topInset = mTmpRect.top;
         // AppBounds at the root level should mirror the app screen size.
         outConfig.windowConfiguration.setAppBounds(leftInset /* left */, topInset /* top */,
-                leftInset + appWidth /* right */, topInset + appHeight /* bottom */);
+                leftInset + appSize.x /* right */, topInset + appSize.y /* bottom */);
         outConfig.windowConfiguration.setRotation(rotation);
         outConfig.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
 
         final float density = mDisplayMetrics.density;
-        outConfig.screenWidthDp = (int) (mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation,
-                uiMode, displayCutout) / density);
-        outConfig.screenHeightDp = (int) (mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation,
-                uiMode, displayCutout) / density);
+        final Point configSize = mDisplayPolicy.getConfigDisplaySize(dw, dh, rotation, uiMode,
+                displayCutout);
+        outConfig.screenWidthDp = (int) (configSize.x / density);
+        outConfig.screenHeightDp = (int) (configSize.y / density);
         outConfig.compatScreenWidthDp = (int) (outConfig.screenWidthDp / mCompatibleScreenScale);
         outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale);
 
@@ -2333,10 +2329,10 @@
             DisplayMetrics dm, int dw, int dh) {
         final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
                 rotation).getDisplayCutout();
-        dm.noncompatWidthPixels = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
+        final Point nonDecorSize = mDisplayPolicy.getNonDecorDisplaySize(dw, dh, rotation, uiMode,
                 displayCutout);
-        dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
-                displayCutout);
+        dm.noncompatWidthPixels = nonDecorSize.x;
+        dm.noncompatHeightPixels = nonDecorSize.y;
         float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
         int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f);
         if (curSize == 0 || size < curSize) {
@@ -2388,12 +2384,12 @@
                 rotation).getDisplayCutout();
 
         // Get the app screen size at this rotation.
-        int w = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayCutout);
-        int h = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayCutout);
+        final Point size = mDisplayPolicy.getNonDecorDisplaySize(dw, dh, rotation, uiMode,
+                displayCutout);
 
         // Compute the screen layout size class for this rotation.
-        int longSize = w;
-        int shortSize = h;
+        int longSize = size.x;
+        int shortSize = size.y;
         if (longSize < shortSize) {
             int tmp = longSize;
             longSize = shortSize;
@@ -2408,21 +2404,19 @@
             int uiMode, int dw, int dh) {
         final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
                 rotation).getDisplayCutout();
-        final int width = mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation, uiMode,
+        final Point size = mDisplayPolicy.getConfigDisplaySize(dw, dh, rotation, uiMode,
                 displayCutout);
-        if (width < displayInfo.smallestNominalAppWidth) {
-            displayInfo.smallestNominalAppWidth = width;
+        if (size.x < displayInfo.smallestNominalAppWidth) {
+            displayInfo.smallestNominalAppWidth = size.x;
         }
-        if (width > displayInfo.largestNominalAppWidth) {
-            displayInfo.largestNominalAppWidth = width;
+        if (size.x > displayInfo.largestNominalAppWidth) {
+            displayInfo.largestNominalAppWidth = size.x;
         }
-        final int height = mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation, uiMode,
-                displayCutout);
-        if (height < displayInfo.smallestNominalAppHeight) {
-            displayInfo.smallestNominalAppHeight = height;
+        if (size.y < displayInfo.smallestNominalAppHeight) {
+            displayInfo.smallestNominalAppHeight = size.y;
         }
-        if (height > displayInfo.largestNominalAppHeight) {
-            displayInfo.largestNominalAppHeight = height;
+        if (size.y > displayInfo.largestNominalAppHeight) {
+            displayInfo.largestNominalAppHeight = size.y;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index b2657e8..1c027a8 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -121,6 +121,7 @@
 import android.content.res.Resources;
 import android.graphics.Insets;
 import android.graphics.PixelFormat;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.gui.DropInputMode;
@@ -2325,6 +2326,24 @@
         }
     }
 
+    private int getAltBarWidth(@InternalInsetsType int insetsType) {
+        final InsetsSource source = mDisplayContent.getInsetsStateController().getRawInsetsState()
+                .peekSource(insetsType);
+        if (source == null) {
+            return 0;
+        }
+        return source.getFrame().width();
+    }
+
+    private int getAltBarHeight(@InternalInsetsType int insetsType) {
+        final InsetsSource source = mDisplayContent.getInsetsStateController().getRawInsetsState()
+                .peekSource(insetsType);
+        if (source == null) {
+            return 0;
+        }
+        return source.getFrame().height();
+    }
+
     void notifyDisplayReady() {
         mHandler.post(() -> {
             final int displayId = getDisplayId();
@@ -2340,26 +2359,6 @@
         });
     }
 
-    /**
-     * Return the display width available after excluding any screen
-     * decorations that could never be removed in Honeycomb. That is, system bar or
-     * button bar.
-     */
-    public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
-            DisplayCutout displayCutout) {
-        int width = fullWidth;
-        if (hasNavigationBar()) {
-            final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
-            if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {
-                width -= getNavigationBarWidth(rotation, uiMode, navBarPosition);
-            }
-        }
-        if (displayCutout != null) {
-            width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight();
-        }
-        return width;
-    }
-
     private int getNavigationBarHeight(int rotation, int uiMode) {
         if (INSETS_LAYOUT_GENERALIZATION) {
             if (mNavigationBar == null) {
@@ -2407,43 +2406,59 @@
     }
 
     /**
-     * Return the display height available after excluding any screen
+     * Return the display size available after excluding any screen
      * decorations that could never be removed in Honeycomb. That is, system bar or
      * button bar.
      */
-    public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
+    Point getNonDecorDisplaySize(int fullWidth, int fullHeight, int rotation, int uiMode,
             DisplayCutout displayCutout) {
+        int width = fullWidth;
         int height = fullHeight;
+        int navBarReducedHeight = 0;
+        int navBarReducedWidth = 0;
+        final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
         if (hasNavigationBar()) {
-            final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
             if (navBarPosition == NAV_BAR_BOTTOM) {
-                height -= getNavigationBarHeight(rotation, uiMode);
+                navBarReducedHeight = getNavigationBarHeight(rotation, uiMode);
+            } else if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {
+                navBarReducedWidth = getNavigationBarWidth(rotation, uiMode, navBarPosition);
             }
         }
+        if (mExtraNavBarAlt != null) {
+            final LayoutParams altBarParams = mExtraNavBarAlt.getLayoutingAttrs(rotation);
+            final int altBarPosition = getAltBarPosition(altBarParams);
+            if (altBarPosition == ALT_BAR_BOTTOM || altBarPosition == ALT_BAR_TOP) {
+                if (altBarPosition == navBarPosition) {
+                    navBarReducedHeight = Math.max(navBarReducedHeight,
+                            getAltBarHeight(ITYPE_EXTRA_NAVIGATION_BAR));
+                } else {
+                    navBarReducedHeight += getAltBarHeight(ITYPE_EXTRA_NAVIGATION_BAR);
+                }
+            } else if (altBarPosition == ALT_BAR_LEFT || altBarPosition == ALT_BAR_RIGHT) {
+                if (altBarPosition == navBarPosition) {
+                    navBarReducedWidth = Math.max(navBarReducedWidth,
+                            getAltBarWidth(ITYPE_EXTRA_NAVIGATION_BAR));
+                } else {
+                    navBarReducedWidth += getAltBarWidth(ITYPE_EXTRA_NAVIGATION_BAR);
+                }
+            }
+        }
+        height -= navBarReducedHeight;
+        width -= navBarReducedWidth;
         if (displayCutout != null) {
             height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom();
+            width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight();
         }
-        return height;
+        return new Point(width, height);
     }
 
     /**
-     * Return the available screen width that we should report for the
+     * Return the available screen size that we should report for the
      * configuration.  This must be no larger than
-     * {@link #getNonDecorDisplayWidth(int, int, int, int, DisplayCutout)}; it may be smaller
+     * {@link #getNonDecorDisplaySize(int, int, int, int, DisplayCutout)}; it may be smaller
      * than that to account for more transient decoration like a status bar.
      */
-    public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
-            DisplayCutout displayCutout) {
-        return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode, displayCutout);
-    }
-
-    /**
-     * Return the available screen height that we should report for the
-     * configuration.  This must be no larger than
-     * {@link #getNonDecorDisplayHeight(int, int, int, int, DisplayCutout)}; it may be smaller
-     * than that to account for more transient decoration like a status bar.
-     */
-    public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
+    Point getConfigDisplaySize(int fullWidth, int fullHeight, int rotation, int uiMode,
             DisplayCutout displayCutout) {
         // There is a separate status bar at the top of the display.  We don't count that as part
         // of the fixed decor, since it can hide; however, for purposes of configurations,
@@ -2455,8 +2470,9 @@
             // bar height.
             statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop());
         }
-        return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayCutout)
-                - statusBarHeight;
+        final Point nonDecorSize = getNonDecorDisplaySize(fullWidth, fullHeight, rotation,
+                uiMode, displayCutout);
+        return new Point(nonDecorSize.x, nonDecorSize.y - statusBarHeight);
     }
 
     /**
@@ -2504,7 +2520,7 @@
 
     /**
      * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system
-     * bar or button bar. See {@link #getNonDecorDisplayWidth}.
+     * bar or button bar. See {@link #getNonDecorDisplaySize}.
      *
      * @param displayRotation the current display rotation
      * @param displayWidth the current display width
@@ -2516,7 +2532,7 @@
             DisplayCutout displayCutout, Rect outInsets) {
         outInsets.setEmpty();
 
-        // Only navigation bar
+        // Only navigation bar and extra navigation bar
         if (hasNavigationBar()) {
             final int uiMode = mService.mPolicy.getUiMode();
             int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
@@ -2528,6 +2544,24 @@
                 outInsets.left = getNavigationBarWidth(displayRotation, uiMode, position);
             }
         }
+        if (mExtraNavBarAlt != null) {
+            final LayoutParams extraNavLayoutParams =
+                    mExtraNavBarAlt.getLayoutingAttrs(displayRotation);
+            final int position = getAltBarPosition(extraNavLayoutParams);
+            if (position == ALT_BAR_BOTTOM) {
+                outInsets.bottom = Math.max(outInsets.bottom,
+                        getAltBarHeight(ITYPE_EXTRA_NAVIGATION_BAR));
+            } else if (position == ALT_BAR_RIGHT) {
+                outInsets.right = Math.max(outInsets.right,
+                        getAltBarWidth(ITYPE_EXTRA_NAVIGATION_BAR));
+            } else if (position == ALT_BAR_LEFT) {
+                outInsets.left = Math.max(outInsets.left,
+                        getAltBarWidth(ITYPE_EXTRA_NAVIGATION_BAR));
+            } else if (position == ALT_BAR_TOP) {
+                outInsets.top = Math.max(outInsets.top,
+                        getAltBarHeight(ITYPE_EXTRA_NAVIGATION_BAR));
+            }
+        }
 
         if (displayCutout != null) {
             outInsets.left += displayCutout.getSafeInsetLeft();
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index cbb473c..212a036 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -30,8 +30,7 @@
 final class LetterboxConfiguration {
 
     /**
-     * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
-     * set-fixed-orientation-letterbox-aspect-ratio or via {@link
+     * Override of aspect ratio for fixed orientation letterboxing that is set via {@link
      * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored
      * if it is <= this value.
      */
@@ -251,7 +250,7 @@
 
     /**
      * Gets {@link LetterboxBackgroundType} specified in {@link
-     * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
+     * com.android.internal.R.integer.config_letterboxBackgroundType}.
      */
     @LetterboxBackgroundType
     int getLetterboxBackgroundType() {
@@ -359,9 +358,8 @@
 
     /*
      * Gets horizontal position of a center of the letterboxed app window specified
-     * in {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}
-     * or via an ADB command. 0 corresponds to the left side of the screen and 1 to the
-     * right side.
+     * in {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}.
+     * 0 corresponds to the left side of the screen and 1 to the right side.
      */
     float getLetterboxHorizontalPositionMultiplier() {
         return (mLetterboxHorizontalPositionMultiplier < 0.0f
@@ -416,8 +414,7 @@
 
     /*
      * Gets default horizontal position of the letterboxed app window when reachability is enabled.
-     * Specified in {@link R.integer.config_letterboxDefaultPositionForReachability} or via an ADB
-     * command.
+     * Specified in {@link R.integer.config_letterboxDefaultPositionForReachability}.
      */
     @LetterboxReachabilityPosition
     int getDefaultPositionForReachability() {
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 94a175c..8a2d116 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -251,15 +251,47 @@
      */
     boolean activityBlockedFromFinish(ActivityRecord activity) {
         final Task task = activity.getTask();
-        if (activity == task.getRootActivity()
-                && activity == task.getTopNonFinishingActivity()
-                && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV
-                && isRootTask(task)) {
-            Slog.i(TAG, "Not finishing task in lock task mode");
-            showLockTaskToast();
-            return true;
+        if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV || !isRootTask(task)) {
+            return false;
         }
-        return false;
+
+        final ActivityRecord taskTop = task.getTopNonFinishingActivity();
+        final ActivityRecord taskRoot = task.getRootActivity();
+        // If task has more than one Activity, verify if there's only adjacent TaskFragments that
+        // should be finish together in the Task.
+        if (activity != taskRoot || activity != taskTop) {
+            final TaskFragment taskFragment = activity.getTaskFragment();
+            final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
+            if (taskFragment.asTask() != null
+                    || !taskFragment.isDelayLastActivityRemoval()
+                    || adjacentTaskFragment == null) {
+                // Don't block activity from finishing if the TaskFragment don't have any adjacent
+                // TaskFragment, or it won't finish together with its adjacent TaskFragment.
+                return false;
+            }
+
+            final boolean hasOtherActivityInTaskFragment =
+                    taskFragment.getActivity(a -> !a.finishing && a != activity) != null;
+            if (hasOtherActivityInTaskFragment) {
+                // Don't block activity from finishing if there's other Activity in the same
+                // TaskFragment.
+                return false;
+            }
+
+            final boolean hasOtherActivityInTask = task.getActivity(a -> !a.finishing
+                    && a != activity && a.getTaskFragment() != adjacentTaskFragment) != null;
+            if (hasOtherActivityInTask) {
+                // Do not block activity from finishing if there are another running activities
+                // after the current and adjacent TaskFragments are removed. Note that we don't
+                // check activities in adjacent TaskFragment because it will be finished together
+                // with TaskFragment regardless of numbers of activities.
+                return false;
+            }
+        }
+
+        Slog.i(TAG, "Not finishing task in lock task mode");
+        showLockTaskToast();
+        return true;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index b54208d..9ad30da 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -255,25 +255,27 @@
         mDestRotatedBounds = null;
         mPipTransaction = null;
         final Rect areaBounds = taskArea.getBounds();
-        if (pipTx != null) {
+        if (pipTx != null && pipTx.mPosition != null) {
             // The transaction from recents animation is in old rotation. So the position needs to
             // be rotated.
-            float dx = pipTx.mPositionX;
-            float dy = pipTx.mPositionY;
+            float dx = pipTx.mPosition.x;
+            float dy = pipTx.mPosition.y;
             final Matrix matrix = pipTx.getMatrix();
             if (pipTx.mRotation == 90) {
-                dx = pipTx.mPositionY;
-                dy = areaBounds.right - pipTx.mPositionX;
+                dx = pipTx.mPosition.y;
+                dy = areaBounds.right - pipTx.mPosition.x;
                 matrix.postRotate(-90);
             } else if (pipTx.mRotation == -90) {
-                dx = areaBounds.bottom - pipTx.mPositionY;
-                dy = pipTx.mPositionX;
+                dx = areaBounds.bottom - pipTx.mPosition.y;
+                dy = pipTx.mPosition.x;
                 matrix.postRotate(90);
             }
             matrix.postTranslate(dx, dy);
             final SurfaceControl leash = pinnedTask.getSurfaceControl();
-            t.setMatrix(leash, matrix, new float[9])
-                    .setCornerRadius(leash, pipTx.mCornerRadius);
+            t.setMatrix(leash, matrix, new float[9]);
+            if (pipTx.hasCornerRadiusSet()) {
+                t.setCornerRadius(leash, pipTx.mCornerRadius);
+            }
             Slog.i(TAG, "Seamless rotation PiP tx=" + pipTx + " pos=" + dx + "," + dy);
             return;
         }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index fd4b63e..5dd8ef3 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -264,13 +264,6 @@
                     "finish(%b): mCanceled=%b", moveHomeToTop, mCanceled);
             final long token = Binder.clearCallingIdentity();
             try {
-                synchronized (mService.getWindowManagerLock()) {
-                    // Remove all new task targets.
-                    for (int i = mPendingNewTaskTargets.size() - 1; i >= 0; i--) {
-                        removeTaskInternal(mPendingNewTaskTargets.get(i));
-                    }
-                }
-
                 // Note, the callback will handle its own synchronization, do not lock on WM lock
                 // prior to calling the callback
                 mCallbacks.onAnimationFinished(moveHomeToTop
@@ -760,7 +753,7 @@
         // the task-id with the leaf id.
         final Task leafTask = task.getTopLeafTask();
         int taskId = leafTask.mTaskId;
-        TaskAnimationAdapter adapter = (TaskAnimationAdapter) addAnimation(task,
+        TaskAnimationAdapter adapter = addAnimation(task,
                 !recentTaskIds.get(taskId), true /* hidden */, finishedCallback);
         mPendingNewTaskTargets.add(taskId);
         return adapter.createRemoteAnimationTarget(taskId);
@@ -1013,6 +1006,7 @@
             taskAdapter.onCleanup();
         }
         // Should already be empty, but clean-up pending task-appears in-case they weren't sent.
+        mPendingNewTaskTargets.clear();
         mPendingTaskAppears.clear();
 
         for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a53a8cd..c88dbf7 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -321,6 +321,11 @@
      */
     boolean mInResumeTopActivity = false;
 
+    /**
+     * Used to identify if the activity that is installed from device's system image.
+     */
+    boolean mIsEffectivelySystemApp;
+
     int mCurrentUser;
 
     String affinity;        // The affinity name for this task, or null; may change identity.
@@ -568,13 +573,24 @@
 
             if (r.finishing) return false;
 
-            // Set this as the candidate root since it isn't finishing.
-            mRoot = r;
+            if (mRoot == null || mRoot.finishing) {
+                // Set this as the candidate root since it isn't finishing.
+                mRoot = r;
+            }
 
-            // Only end search if we are ignore relinquishing identity or we are not relinquishing.
-            return ignoreRelinquishIdentity
-                    || mNeverRelinquishIdentity
-                    || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
+            final int uid = mRoot == r ? effectiveUid : r.info.applicationInfo.uid;
+            if (ignoreRelinquishIdentity
+                    || (mRoot.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0
+                    || (mRoot.info.applicationInfo.uid != Process.SYSTEM_UID
+                    && !mRoot.info.applicationInfo.isSystemApp()
+                    && mRoot.info.applicationInfo.uid != uid)) {
+                // No need to relinquish identity, end search.
+                return true;
+            }
+
+            // Relinquish to next activity
+            mRoot = r;
+            return false;
         }
     }
 
@@ -999,7 +1015,15 @@
      * @param info The activity info which could be different from {@code r.info} if set.
      */
     void setIntent(ActivityRecord r, @Nullable Intent intent, @Nullable ActivityInfo info) {
-        if (this.intent == null || !mNeverRelinquishIdentity) {
+        boolean updateIdentity = false;
+        if (this.intent == null) {
+            updateIdentity = true;
+        } else if (!mNeverRelinquishIdentity) {
+            final ActivityInfo activityInfo = info != null ? info : r.info;
+            updateIdentity = (effectiveUid == Process.SYSTEM_UID || mIsEffectivelySystemApp
+                    || effectiveUid == activityInfo.applicationInfo.uid);
+        }
+        if (updateIdentity) {
             mCallingUid = r.launchedFromUid;
             mCallingPackage = r.launchedFromPackage;
             mCallingFeatureId = r.launchedFromFeatureId;
@@ -1012,14 +1036,7 @@
     private void setIntent(Intent _intent, ActivityInfo info) {
         if (!isLeafTask()) return;
 
-        if (info.applicationInfo.uid == Process.SYSTEM_UID
-                || info.applicationInfo.isSystemApp()) {
-            // Only allow the apps that pre-installed on the system image to apply
-            // relinquishTaskIdentity
-            mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
-        } else {
-            mNeverRelinquishIdentity = true;
-        }
+        mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
         affinity = info.taskAffinity;
         if (intent == null) {
             // If this task already has an intent associated with it, don't set the root
@@ -1028,6 +1045,7 @@
             rootAffinity = affinity;
         }
         effectiveUid = info.applicationInfo.uid;
+        mIsEffectivelySystemApp = info.applicationInfo.isSystemApp();
         stringName = null;
 
         if (info.targetActivity == null) {
@@ -1451,11 +1469,11 @@
     }
 
     /** Called when an {@link ActivityRecord} is added as a descendant */
-    void onDescendantActivityAdded(boolean hadChild, int activityType, ActivityRecord r) {
+    void onDescendantActivityAdded(boolean hadActivity, int activityType, ActivityRecord r) {
         warnForNonLeafTask("onDescendantActivityAdded");
 
         // Only set this based on the first activity
-        if (!hadChild) {
+        if (!hadActivity) {
             if (r.getActivityType() == ACTIVITY_TYPE_UNDEFINED) {
                 // Normally non-standard activity type for the activity record will be set when the
                 // object is created, however we delay setting the standard application type until
@@ -3141,14 +3159,6 @@
     }
 
     @Override
-    boolean fillsParent() {
-        // From the perspective of policy, we still want to report that this task fills parent
-        // in fullscreen windowing mode even it doesn't match parent bounds because there will be
-        // letterbox around its real content.
-        return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds();
-    }
-
-    @Override
     void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
         final int count = mChildren.size();
         boolean isLeafTask = true;
@@ -4581,14 +4591,15 @@
             }
             super.setWindowingMode(windowingMode);
 
-            // Try reparent pinned activity back to its original task after onConfigurationChanged
-            // cascade finishes. This is done on Task level instead of
-            // {@link ActivityRecord#onConfigurationChanged(Configuration)} since when we exit PiP,
-            // we set final windowing mode on the ActivityRecord first and then on its Task when
-            // the exit PiP transition finishes. Meanwhile, the exit transition is always
-            // performed on its original task, reparent immediately in ActivityRecord breaks it.
-            if (currentMode == WINDOWING_MODE_PINNED) {
-                if (topActivity != null && topActivity.getLastParentBeforePip() != null) {
+            if (currentMode == WINDOWING_MODE_PINNED && topActivity != null) {
+                // Try reparent pinned activity back to its original task after
+                // onConfigurationChanged cascade finishes. This is done on Task level instead of
+                // {@link ActivityRecord#onConfigurationChanged(Configuration)} since when we exit
+                // PiP, we set final windowing mode on the ActivityRecord first and then on its
+                // Task when the exit PiP transition finishes. Meanwhile, the exit transition is
+                // always performed on its original task, reparent immediately in ActivityRecord
+                // breaks it.
+                if (topActivity.getLastParentBeforePip() != null) {
                     // Do not reparent if the pinned task is in removal, indicated by the
                     // force hidden flag.
                     if (!isForceHidden()) {
@@ -4601,6 +4612,11 @@
                         }
                     }
                 }
+                // Resume app-switches-allowed flag when exiting from pinned mode since
+                // it does not follow the ActivityStarter path.
+                if (topActivity.shouldBeVisible()) {
+                    mAtmService.resumeAppSwitches();
+                }
             }
 
             if (creating) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 510ca77..916fa5b 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -102,6 +102,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
@@ -240,7 +241,7 @@
     /**
      * Whether to delay the last activity of TaskFragment being immediately removed while finishing.
      * This should only be set on a embedded TaskFragment, where the organizer can have the
-     * opportunity to perform other actions or animations.
+     * opportunity to perform animations and finishing the adjacent TaskFragment.
      */
     private boolean mDelayLastActivityRemoval;
 
@@ -1670,8 +1671,8 @@
         boolean isAddingActivity = child.asActivityRecord() != null;
         final Task task = isAddingActivity ? getTask() : null;
 
-        // If this task had any child before we added this one.
-        boolean taskHadChild = task != null && task.hasChild();
+        // If this task had any activity before we added this one.
+        boolean taskHadActivity = task != null && task.getActivity(Objects::nonNull) != null;
         // getActivityType() looks at the top child, so we need to read the type before adding
         // a new child in case the new child is on top and UNDEFINED.
         final int activityType = task != null ? task.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
@@ -1680,7 +1681,7 @@
 
         if (isAddingActivity && task != null) {
             child.asActivityRecord().inHistory = true;
-            task.onDescendantActivityAdded(taskHadChild, activityType, child.asActivityRecord());
+            task.onDescendantActivityAdded(taskHadActivity, activityType, child.asActivityRecord());
         }
     }
 
@@ -2190,14 +2191,13 @@
     TaskFragmentInfo getTaskFragmentInfo() {
         List<IBinder> childActivities = new ArrayList<>();
         for (int i = 0; i < getChildCount(); i++) {
-            WindowContainer wc = getChildAt(i);
-            if (mTaskFragmentOrganizerUid != INVALID_UID
-                    && wc.asActivityRecord() != null
-                    && wc.asActivityRecord().info.processName.equals(
-                            mTaskFragmentOrganizerProcessName)
-                    && wc.asActivityRecord().getUid() == mTaskFragmentOrganizerUid) {
+            final WindowContainer wc = getChildAt(i);
+            final ActivityRecord ar = wc.asActivityRecord();
+            if (mTaskFragmentOrganizerUid != INVALID_UID && ar != null
+                    && ar.info.processName.equals(mTaskFragmentOrganizerProcessName)
+                    && ar.getUid() == mTaskFragmentOrganizerUid && !ar.finishing) {
                 // Only includes Activities that belong to the organizer process for security.
-                childActivities.add(wc.asActivityRecord().appToken);
+                childActivities.add(ar.appToken);
             }
         }
         final Point positionInParent = new Point();
@@ -2346,6 +2346,14 @@
         return true;
     }
 
+    @Override
+    boolean fillsParent() {
+        // From the perspective of policy, we still want to report that this task fills parent
+        // in fullscreen windowing mode even it doesn't match parent bounds because there will be
+        // letterbox around its real content.
+        return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds();
+    }
+
     boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll,
             boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) {
         boolean printed = false;
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 29c27f9..c7fdefc 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -24,6 +24,7 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -579,4 +580,26 @@
                         event.mException);
         }
     }
+
+    // TODO(b/204399167): change to push the embedded state to the client side
+    @Override
+    public boolean isActivityEmbedded(IBinder activityToken) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord activity = ActivityRecord.forTokenLocked(activityToken);
+            if (activity == null) {
+                return false;
+            }
+            final TaskFragment taskFragment = activity.getOrganizedTaskFragment();
+            if (taskFragment == null) {
+                return false;
+            }
+            final Task parentTask = taskFragment.getTask();
+            if (parentTask != null) {
+                final Rect taskBounds = parentTask.getBounds();
+                final Rect taskFragBounds = taskFragment.getBounds();
+                return !taskBounds.equals(taskFragBounds) && taskBounds.contains(taskFragBounds);
+            }
+            return false;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index cc52713..7956a11 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -173,7 +173,7 @@
 
     @VisibleForTesting
     class WindowContextListenerImpl implements WindowContainerListener {
-        @NonNull private final IBinder mClientToken;
+        @NonNull private final IWindowToken mClientToken;
         private final int mOwnerUid;
         @NonNull private WindowContainer<?> mContainer;
         /**
@@ -193,7 +193,7 @@
 
         private WindowContextListenerImpl(IBinder clientToken, WindowContainer<?> container,
                 int ownerUid, @WindowType int type, @Nullable Bundle options) {
-            mClientToken = clientToken;
+            mClientToken = IWindowToken.Stub.asInterface(clientToken);
             mContainer = Objects.requireNonNull(container);
             mOwnerUid = ownerUid;
             mType = type;
@@ -205,7 +205,7 @@
                 mDeathRecipient = deathRecipient;
             } catch (RemoteException e) {
                 ProtoLog.e(WM_ERROR, "Could not register window container listener token=%s, "
-                        + "container=%s", mClientToken, mContainer);
+                        + "container=%s", clientToken, mContainer);
             }
         }
 
@@ -228,17 +228,17 @@
         }
 
         private void register() {
+            final IBinder token = mClientToken.asBinder();
             if (mDeathRecipient == null) {
-                throw new IllegalStateException("Invalid client token: " + mClientToken);
+                throw new IllegalStateException("Invalid client token: " + token);
             }
-            mListeners.putIfAbsent(mClientToken, this);
+            mListeners.putIfAbsent(token, this);
             mContainer.registerWindowContainerListener(this);
-            reportConfigToWindowTokenClient();
         }
 
         private void unregister() {
             mContainer.unregisterWindowContainerListener(this);
-            mListeners.remove(mClientToken);
+            mListeners.remove(mClientToken.asBinder());
         }
 
         private void clear() {
@@ -258,19 +258,24 @@
 
         private void reportConfigToWindowTokenClient() {
             if (mDeathRecipient == null) {
-                throw new IllegalStateException("Invalid client token: " + mClientToken);
+                throw new IllegalStateException("Invalid client token: " + mClientToken.asBinder());
+            }
+            final DisplayContent dc = mContainer.getDisplayContent();
+            if (!dc.isReady()) {
+                // Do not report configuration when booting. The latest configuration will be sent
+                // when WindowManagerService#displayReady().
+                return;
             }
             // If the display of window context associated window container is suspended, don't
             // report the configuration update. Note that we still dispatch the configuration update
             // to WindowProviderService to make it compatible with Service#onConfigurationChanged.
             // Service always receives #onConfigurationChanged callback regardless of display state.
-            if (!isWindowProviderService(mOptions)
-                    && isSuspendedState(mContainer.getDisplayContent().getDisplayInfo().state)) {
+            if (!isWindowProviderService(mOptions) && isSuspendedState(dc.getDisplayInfo().state)) {
                 mHasPendingConfiguration = true;
                 return;
             }
             final Configuration config = mContainer.getConfiguration();
-            final int displayId = mContainer.getDisplayContent().getDisplayId();
+            final int displayId = dc.getDisplayId();
             if (mLastReportedConfig == null) {
                 mLastReportedConfig = new Configuration();
             }
@@ -282,9 +287,8 @@
             mLastReportedConfig.setTo(config);
             mLastReportedDisplay = displayId;
 
-            IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken);
             try {
-                windowTokenClient.onConfigurationChanged(config, displayId);
+                mClientToken.onConfigurationChanged(config, displayId);
             } catch (RemoteException e) {
                 ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client.");
             }
@@ -294,7 +298,7 @@
         @Override
         public void onRemoved() {
             if (mDeathRecipient == null) {
-                throw new IllegalStateException("Invalid client token: " + mClientToken);
+                throw new IllegalStateException("Invalid client token: " + mClientToken.asBinder());
             }
             final WindowToken windowToken = mContainer.asWindowToken();
             if (windowToken != null && windowToken.isFromClient()) {
@@ -312,9 +316,8 @@
                 }
             }
             mDeathRecipient.unlinkToDeath();
-            IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken);
             try {
-                windowTokenClient.onWindowTokenRemoved();
+                mClientToken.onWindowTokenRemoved();
             } catch (RemoteException e) {
                 ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client.");
             }
@@ -323,7 +326,7 @@
 
         @Override
         public String toString() {
-            return "WindowContextListenerImpl{clientToken=" + mClientToken + ", "
+            return "WindowContextListenerImpl{clientToken=" + mClientToken.asBinder() + ", "
                     + "container=" + mContainer + "}";
         }
 
@@ -337,11 +340,11 @@
             }
 
             void linkToDeath() throws RemoteException {
-                mClientToken.linkToDeath(this, 0);
+                mClientToken.asBinder().linkToDeath(this, 0);
             }
 
             void unlinkToDeath() {
-                mClientToken.unlinkToDeath(this, 0);
+                mClientToken.asBinder().unlinkToDeath(this, 0);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fa47700..696513c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6171,9 +6171,10 @@
                         + " callers=" + Debug.getCallers(3));
                 return NAV_BAR_INVALID;
             }
-            displayContent.performLayout(false /* initial */,
-                    false /* updateInputWindows */);
-            return displayContent.getDisplayPolicy().getNavBarPosition();
+            return displayContent.getDisplayPolicy().navigationBarPosition(
+                displayContent.mBaseDisplayWidth,
+                displayContent.mBaseDisplayHeight,
+                displayContent.getDisplayRotation().getRotation());
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 0f8587c..a94fd07 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -19,16 +19,6 @@
 import static android.os.Build.IS_USER;
 import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
 
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_CENTER;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_LEFT;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_RIGHT;
-
-import android.content.res.Resources.NotFoundException;
-import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.ParcelFileDescriptor;
@@ -46,8 +36,6 @@
 import com.android.internal.protolog.ProtoLogImpl;
 import com.android.server.LocalServices;
 import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
-import com.android.server.wm.LetterboxConfiguration.LetterboxReachabilityPosition;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -70,12 +58,10 @@
 
     // Internal service impl -- must perform security checks before touching.
     private final WindowManagerService mInternal;
-    private final LetterboxConfiguration mLetterboxConfiguration;
 
     public WindowManagerShellCommand(WindowManagerService service) {
         mInterface = service;
         mInternal = service;
-        mLetterboxConfiguration = service.mLetterboxConfiguration;
     }
 
     @Override
@@ -127,14 +113,6 @@
                     return runGetIgnoreOrientationRequest(pw);
                 case "dump-visible-window-views":
                     return runDumpVisibleWindowViews(pw);
-                case "set-letterbox-style":
-                    return runSetLetterboxStyle(pw);
-                case "get-letterbox-style":
-                    return runGetLetterboxStyle(pw);
-                case "reset-letterbox-style":
-                    return runResetLetterboxStyle(pw);
-                case "set-sandbox-display-apis":
-                    return runSandboxDisplayApis(pw);
                 case "set-multi-window-config":
                     return runSetMultiWindowConfig();
                 case "get-multi-window-config":
@@ -353,37 +331,6 @@
         return 0;
     }
 
-    /**
-     * Override display size and metrics to reflect the DisplayArea of the calling activity.
-     */
-    private int runSandboxDisplayApis(PrintWriter pw) throws RemoteException {
-        int displayId = Display.DEFAULT_DISPLAY;
-        String arg = getNextArgRequired();
-        if ("-d".equals(arg)) {
-            displayId = Integer.parseInt(getNextArgRequired());
-            arg = getNextArgRequired();
-        }
-
-        final boolean sandboxDisplayApis;
-        switch (arg) {
-            case "true":
-            case "1":
-                sandboxDisplayApis = true;
-                break;
-            case "false":
-            case "0":
-                sandboxDisplayApis = false;
-                break;
-            default:
-                getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we "
-                        + "get " + arg);
-                return -1;
-        }
-
-        mInternal.setSandboxDisplayApis(displayId, sandboxDisplayApis);
-        return 0;
-    }
-
     private int runDismissKeyguard(PrintWriter pw) throws RemoteException {
         mInterface.dismissKeyguard(null /* callback */, null /* message */);
         return 0;
@@ -601,318 +548,6 @@
         return 0;
     }
 
-    private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
-        final float aspectRatio;
-        try {
-            String arg = getNextArgRequired();
-            aspectRatio = Float.parseFloat(arg);
-        } catch (NumberFormatException  e) {
-            getErrPrintWriter().println("Error: bad aspect ratio format " + e);
-            return -1;
-        } catch (IllegalArgumentException  e) {
-            getErrPrintWriter().println(
-                    "Error: aspect ratio should be provided as an argument " + e);
-            return -1;
-        }
-        synchronized (mInternal.mGlobalLock) {
-            mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio);
-        }
-        return 0;
-    }
-
-    private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
-        final int cornersRadius;
-        try {
-            String arg = getNextArgRequired();
-            cornersRadius = Integer.parseInt(arg);
-        } catch (NumberFormatException  e) {
-            getErrPrintWriter().println("Error: bad corners radius format " + e);
-            return -1;
-        } catch (IllegalArgumentException  e) {
-            getErrPrintWriter().println(
-                    "Error: corners radius should be provided as an argument " + e);
-            return -1;
-        }
-        synchronized (mInternal.mGlobalLock) {
-            mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius);
-        }
-        return 0;
-    }
-
-    private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
-        @LetterboxBackgroundType final int backgroundType;
-        try {
-            String arg = getNextArgRequired();
-            switch (arg) {
-                case "solid_color":
-                    backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
-                    break;
-                case "app_color_background":
-                    backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
-                    break;
-                case "app_color_background_floating":
-                    backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
-                    break;
-                case "wallpaper":
-                    backgroundType = LETTERBOX_BACKGROUND_WALLPAPER;
-                    break;
-                default:
-                    getErrPrintWriter().println(
-                            "Error: 'solid_color', 'app_color_background' or "
-                            + "'wallpaper' should be provided as an argument");
-                    return -1;
-            }
-        } catch (IllegalArgumentException  e) {
-            getErrPrintWriter().println(
-                    "Error: 'solid_color', 'app_color_background' or "
-                        + "'wallpaper' should be provided as an argument" + e);
-            return -1;
-        }
-        synchronized (mInternal.mGlobalLock) {
-            mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType);
-        }
-        return 0;
-    }
-
-    private int runSetLetterboxBackgroundColorResource(PrintWriter pw) throws RemoteException {
-        final int colorId;
-        try {
-            String arg = getNextArgRequired();
-            colorId = mInternal.mContext.getResources()
-                    .getIdentifier(arg, "color", "com.android.internal");
-        } catch (NotFoundException e) {
-            getErrPrintWriter().println(
-                    "Error: color in '@android:color/resource_name' format should be provided as "
-                            + "an argument " + e);
-            return -1;
-        }
-        synchronized (mInternal.mGlobalLock) {
-            mLetterboxConfiguration.setLetterboxBackgroundColorResourceId(colorId);
-        }
-        return 0;
-    }
-
-    private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
-        final Color color;
-        try {
-            String arg = getNextArgRequired();
-            color = Color.valueOf(Color.parseColor(arg));
-        } catch (IllegalArgumentException  e) {
-            getErrPrintWriter().println(
-                    "Error: color in #RRGGBB format should be provided as "
-                            + "an argument " + e);
-            return -1;
-        }
-        synchronized (mInternal.mGlobalLock) {
-            mLetterboxConfiguration.setLetterboxBackgroundColor(color);
-        }
-        return 0;
-    }
-
-    private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw)
-            throws RemoteException {
-        final int radius;
-        try {
-            String arg = getNextArgRequired();
-            radius = Integer.parseInt(arg);
-        } catch (NumberFormatException  e) {
-            getErrPrintWriter().println("Error: blur radius format " + e);
-            return -1;
-        } catch (IllegalArgumentException  e) {
-            getErrPrintWriter().println(
-                    "Error: blur radius should be provided as an argument " + e);
-            return -1;
-        }
-        synchronized (mInternal.mGlobalLock) {
-            mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius);
-        }
-        return 0;
-    }
-
-    private int runSetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw)
-            throws RemoteException {
-        final float alpha;
-        try {
-            String arg = getNextArgRequired();
-            alpha = Float.parseFloat(arg);
-        } catch (NumberFormatException  e) {
-            getErrPrintWriter().println("Error: bad alpha format " + e);
-            return -1;
-        } catch (IllegalArgumentException  e) {
-            getErrPrintWriter().println(
-                    "Error: alpha should be provided as an argument " + e);
-            return -1;
-        }
-        synchronized (mInternal.mGlobalLock) {
-            mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha);
-        }
-        return 0;
-    }
-
-    private int runSetLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException {
-        final float multiplier;
-        try {
-            String arg = getNextArgRequired();
-            multiplier = Float.parseFloat(arg);
-        } catch (NumberFormatException  e) {
-            getErrPrintWriter().println("Error: bad multiplier format " + e);
-            return -1;
-        } catch (IllegalArgumentException  e) {
-            getErrPrintWriter().println(
-                    "Error: multiplier should be provided as an argument " + e);
-            return -1;
-        }
-        synchronized (mInternal.mGlobalLock) {
-            mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier);
-        }
-        return 0;
-    }
-
-    private int runSetLetterboxIsReachabilityEnabled(PrintWriter pw) throws RemoteException {
-        String arg = getNextArg();
-        final boolean enabled;
-        switch (arg) {
-            case "true":
-            case "1":
-                enabled = true;
-                break;
-            case "false":
-            case "0":
-                enabled = false;
-                break;
-            default:
-                getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg);
-                return -1;
-        }
-
-        synchronized (mInternal.mGlobalLock) {
-            mLetterboxConfiguration.setIsReachabilityEnabled(enabled);
-        }
-        return 0;
-    }
-
-    private int runSetLetterboxDefaultPositionForReachability(PrintWriter pw)
-            throws RemoteException {
-        @LetterboxReachabilityPosition final int position;
-        try {
-            String arg = getNextArgRequired();
-            switch (arg) {
-                case "left":
-                    position = LETTERBOX_REACHABILITY_POSITION_LEFT;
-                    break;
-                case "center":
-                    position = LETTERBOX_REACHABILITY_POSITION_CENTER;
-                    break;
-                case "right":
-                    position = LETTERBOX_REACHABILITY_POSITION_RIGHT;
-                    break;
-                default:
-                    getErrPrintWriter().println(
-                            "Error: 'left', 'center' or 'right' are expected as an argument");
-                    return -1;
-            }
-        } catch (IllegalArgumentException  e) {
-            getErrPrintWriter().println(
-                    "Error: 'left', 'center' or 'right' are expected as an argument" + e);
-            return -1;
-        }
-        synchronized (mInternal.mGlobalLock) {
-            mLetterboxConfiguration.setDefaultPositionForReachability(position);
-        }
-        return 0;
-    }
-
-    private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
-        if (peekNextArg() == null) {
-            getErrPrintWriter().println("Error: No arguments provided.");
-        }
-        while (peekNextArg() != null) {
-            String arg = getNextArg();
-            switch (arg) {
-                case "--aspectRatio":
-                    runSetFixedOrientationLetterboxAspectRatio(pw);
-                    break;
-                case "--cornerRadius":
-                    runSetLetterboxActivityCornersRadius(pw);
-                    break;
-                case "--backgroundType":
-                    runSetLetterboxBackgroundType(pw);
-                    break;
-                case "--backgroundColor":
-                    runSetLetterboxBackgroundColor(pw);
-                    break;
-                case "--backgroundColorResource":
-                    runSetLetterboxBackgroundColorResource(pw);
-                    break;
-                case "--wallpaperBlurRadius":
-                    runSetLetterboxBackgroundWallpaperBlurRadius(pw);
-                    break;
-                case "--wallpaperDarkScrimAlpha":
-                    runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
-                    break;
-                case "--horizontalPositionMultiplier":
-                    runSetLetterboxHorizontalPositionMultiplier(pw);
-                    break;
-                case "--isReachabilityEnabled":
-                    runSetLetterboxIsReachabilityEnabled(pw);
-                    break;
-                case "--defaultPositionForReachability":
-                    runSetLetterboxDefaultPositionForReachability(pw);
-                    break;
-                default:
-                    getErrPrintWriter().println(
-                            "Error: Unrecognized letterbox style option: " + arg);
-                    return -1;
-            }
-        }
-        return 0;
-    }
-
-    private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException {
-        if (peekNextArg() == null) {
-            resetLetterboxStyle();
-        }
-        synchronized (mInternal.mGlobalLock) {
-            while (peekNextArg() != null) {
-                String arg = getNextArg();
-                switch (arg) {
-                    case "aspectRatio":
-                        mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
-                        break;
-                    case "cornerRadius":
-                        mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
-                        break;
-                    case "backgroundType":
-                        mLetterboxConfiguration.resetLetterboxBackgroundType();
-                        break;
-                    case "backgroundColor":
-                        mLetterboxConfiguration.resetLetterboxBackgroundColor();
-                        break;
-                    case "wallpaperBlurRadius":
-                        mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
-                        break;
-                    case "wallpaperDarkScrimAlpha":
-                        mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
-                        break;
-                    case "horizontalPositionMultiplier":
-                        mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
-                        break;
-                    case "isReachabilityEnabled":
-                        mLetterboxConfiguration.getIsReachabilityEnabled();
-                        break;
-                    case "defaultPositionForReachability":
-                        mLetterboxConfiguration.getDefaultPositionForReachability();
-                        break;
-                    default:
-                        getErrPrintWriter().println(
-                                "Error: Unrecognized letterbox style option: " + arg);
-                        return -1;
-                }
-            }
-        }
-        return 0;
-    }
-
     private int runSetMultiWindowConfig() {
         if (peekNextArg() == null) {
             getErrPrintWriter().println("Error: No arguments provided.");
@@ -987,47 +622,6 @@
         return 0;
     }
 
-    private void resetLetterboxStyle() {
-        synchronized (mInternal.mGlobalLock) {
-            mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
-            mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
-            mLetterboxConfiguration.resetLetterboxBackgroundType();
-            mLetterboxConfiguration.resetLetterboxBackgroundColor();
-            mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
-            mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
-            mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
-            mLetterboxConfiguration.resetIsReachabilityEnabled();
-            mLetterboxConfiguration.resetDefaultPositionForReachability();
-        }
-    }
-
-    private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException {
-        synchronized (mInternal.mGlobalLock) {
-            pw.println("Corner radius: "
-                    + mLetterboxConfiguration.getLetterboxActivityCornersRadius());
-            pw.println("Horizontal position multiplier: "
-                    + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
-            pw.println("Aspect ratio: "
-                    + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
-            pw.println("Is reachability enabled: "
-                    + mLetterboxConfiguration.getIsReachabilityEnabled());
-            pw.println("Default position for reachability: "
-                    + LetterboxConfiguration.letterboxReachabilityPositionToString(
-                            mLetterboxConfiguration.getDefaultPositionForReachability()));
-
-            pw.println("Background type: "
-                    + LetterboxConfiguration.letterboxBackgroundTypeToString(
-                            mLetterboxConfiguration.getLetterboxBackgroundType()));
-            pw.println("    Background color: " + Integer.toHexString(
-                    mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb()));
-            pw.println("    Wallpaper blur radius: "
-                    + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius());
-            pw.println("    Wallpaper dark scrim alpha: "
-                    + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha());
-        }
-        return 0;
-    }
-
     private int runReset(PrintWriter pw) throws RemoteException {
         int displayId = getDisplayId(getNextArg());
 
@@ -1052,12 +646,6 @@
         // set-ignore-orientation-request
         mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
 
-        // set-letterbox-style
-        resetLetterboxStyle();
-
-        // set-sandbox-display-apis
-        mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true);
-
         // set-multi-window-config
         runResetMultiWindowConfig();
 
@@ -1092,12 +680,7 @@
         pw.println("  set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
         pw.println("  get-ignore-orientation-request [-d DISPLAY_ID] ");
         pw.println("    If app requested orientation should be ignored.");
-        pw.println("  set-sandbox-display-apis [true|1|false|0]");
-        pw.println("    Sets override of Display APIs getRealSize / getRealMetrics to reflect ");
-        pw.println("    DisplayArea of the activity, or the window bounds if in letterbox or");
-        pw.println("    Size Compat Mode.");
 
-        printLetterboxHelp(pw);
         printMultiWindowConfigHelp(pw);
 
         pw.println("  reset [-d DISPLAY_ID]");
@@ -1110,61 +693,6 @@
         }
     }
 
-    private void printLetterboxHelp(PrintWriter pw) {
-        pw.println("  set-letterbox-style");
-        pw.println("    Sets letterbox style using the following options:");
-        pw.println("      --aspectRatio aspectRatio");
-        pw.println("        Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
-                + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
-        pw.println("        both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will");
-        pw.println("        be ignored and framework implementation will determine aspect ratio.");
-        pw.println("      --cornerRadius radius");
-        pw.println("        Corners radius for activities in the letterbox mode. If radius < 0,");
-        pw.println("        both it and R.integer.config_letterboxActivityCornersRadius will be");
-        pw.println("        ignored and corners of the activity won't be rounded.");
-        pw.println("      --backgroundType [reset|solid_color|app_color_background");
-        pw.println("          |app_color_background_floating|wallpaper]");
-        pw.println("        Type of background used in the letterbox mode.");
-        pw.println("      --backgroundColor color");
-        pw.println("        Color of letterbox which is be used when letterbox background type");
-        pw.println("        is 'solid-color'. Use (set)get-letterbox-style to check and control");
-        pw.println("        letterbox background type. See Color#parseColor for allowed color");
-        pw.println("        formats (#RRGGBB and some colors by name, e.g. magenta or olive).");
-        pw.println("      --backgroundColorResource resource_name");
-        pw.println("        Color resource name of letterbox background which is used when");
-        pw.println("        background type is 'solid-color'. Use (set)get-letterbox-style to");
-        pw.println("        check and control background type. Parameter is a color resource");
-        pw.println("        name, for example, @android:color/system_accent2_50.");
-        pw.println("      --wallpaperBlurRadius radius");
-        pw.println("        Blur radius for 'wallpaper' letterbox background. If radius <= 0");
-        pw.println("        both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius");
-        pw.println("        are ignored and 0 is used.");
-        pw.println("      --wallpaperDarkScrimAlpha alpha");
-        pw.println("        Alpha of a black translucent scrim shown over 'wallpaper'");
-        pw.println("        letterbox background. If alpha < 0 or >= 1 both it and");
-        pw.println("        R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored");
-        pw.println("        and 0.0 (transparent) is used instead.");
-        pw.println("      --horizontalPositionMultiplier multiplier");
-        pw.println("        Horizontal position of app window center. If multiplier < 0 or > 1,");
-        pw.println("        both it and R.dimen.config_letterboxHorizontalPositionMultiplier");
-        pw.println("        are ignored and central position (0.5) is used.");
-        pw.println("      --isReachabilityEnabled [true|1|false|0]");
-        pw.println("        Whether reachability repositioning is allowed for letterboxed");
-        pw.println("        fullscreen apps in landscape device orientation.");
-        pw.println("      --defaultPositionForReachability [left|center|right]");
-        pw.println("        Default horizontal position of app window  when reachability is.");
-        pw.println("        enabled.");
-        pw.println("  reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
-        pw.println("      |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
-        pw.println("      |horizontalPositionMultiplier|isReachabilityEnabled");
-        pw.println("      |defaultPositionMultiplierForReachability]");
-        pw.println("    Resets overrides to default values for specified properties separated");
-        pw.println("    by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
-        pw.println("    If no arguments provided, all values will be reset.");
-        pw.println("  get-letterbox-style");
-        pw.println("    Prints letterbox style configuration.");
-    }
-
     private void printMultiWindowConfigHelp(PrintWriter pw) {
         pw.println("  set-multi-window-config");
         pw.println("    Sets options to determine if activity should be shown in multi window:");
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 1cfbe07..3ccb06c 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -512,19 +512,19 @@
      */
     @HotPath(caller = HotPath.START_SERVICE)
     public boolean areBackgroundFgsStartsAllowed() {
-        return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesAllowed(),
+        return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesState(),
                 true /* isCheckingForFgsStart */);
     }
 
-    boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed) {
-        return areBackgroundActivityStartsAllowed(appSwitchAllowed,
+    boolean areBackgroundActivityStartsAllowed(int appSwitchState) {
+        return areBackgroundActivityStartsAllowed(appSwitchState,
                 false /* isCheckingForFgsStart */);
     }
 
-    private boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed,
+    private boolean areBackgroundActivityStartsAllowed(int appSwitchState,
             boolean isCheckingForFgsStart) {
         return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid, mInfo.packageName,
-                appSwitchAllowed, isCheckingForFgsStart, hasActivityInVisibleTask(),
+                appSwitchState, isCheckingForFgsStart, hasActivityInVisibleTask(),
                 mInstrumentingWithBackgroundActivityStartPrivileges,
                 mAtm.getLastStopAppSwitchesTime(),
                 mLastActivityLaunchTime, mLastActivityFinishTime);
diff --git a/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java
index 49d5e50..d3353cd 100644
--- a/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java
+++ b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java
@@ -31,6 +31,7 @@
 
 import com.android.server.LocalServices;
 import com.android.server.people.PeopleServiceInternal;
+import com.android.server.pm.PackageManagerService;
 
 /**
  * If a {@link ConversationStatus} is added to the system with an expiration time, remove that
@@ -50,6 +51,7 @@
             final PendingIntent pi = PendingIntent.getBroadcast(context,
                     REQUEST_CODE,
                     new Intent(ACTION)
+                            .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
                             .setData(new Uri.Builder().scheme(SCHEME)
                                     .appendPath(getKey(userId, pkg, conversationId, status))
                                     .build())
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 18f1267..0dd4f5b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
@@ -1854,6 +1855,36 @@
 
     @SuppressWarnings("GuardedBy")
     @Test
+    public void testUpdateOomAdj_DoAll_BoundByPersService_Cycle_Branch_Capability() {
+        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+        ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+                MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+        bindService(app, client, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+        ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
+                MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
+        bindService(client, client2, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+        bindService(client2, app, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+        ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
+                MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
+        client3.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+        bindService(app, client3, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+        ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
+        lru.clear();
+        lru.add(app);
+        lru.add(client);
+        lru.add(client2);
+        lru.add(client3);
+        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+        assertEquals(PROCESS_CAPABILITY_ALL, client.mState.getSetCapability());
+        assertEquals(PROCESS_CAPABILITY_ALL, client2.mState.getSetCapability());
+        assertEquals(PROCESS_CAPABILITY_ALL, app.mState.getSetCapability());
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
     public void testUpdateOomAdj_DoAll_Provider_Cycle_Branch_2() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
index 4d6f49e..4eba219 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
@@ -90,6 +90,19 @@
     }
 
     @Test
+    public void testThrottle_lowInterval() {
+        ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(0).build();
+
+        mProvider.getController().setRequest(request);
+        mDelegateProvider.reportLocation(createLocationResult("test_provider", mRandom));
+        verify(mListener, times(1)).onReportLocation(any(LocationResult.class));
+
+        mInjector.getDeviceStationaryHelper().setStationary(true);
+        mInjector.getDeviceIdleHelper().setIdle(true);
+        verify(mListener, after(1500).times(2)).onReportLocation(any(LocationResult.class));
+    }
+
+    @Test
     public void testThrottle_stationaryExit() {
         ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
 
@@ -104,17 +117,16 @@
 
         mInjector.getDeviceIdleHelper().setIdle(true);
         verify(mDelegate).onSetRequest(ProviderRequest.EMPTY_REQUEST);
-        verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
-        verify(mListener, timeout(75).times(3)).onReportLocation(any(LocationResult.class));
+        verify(mListener, timeout(1100).times(2)).onReportLocation(any(LocationResult.class));
 
         mInjector.getDeviceStationaryHelper().setStationary(false);
         verify(mDelegate, times(2)).onSetRequest(request);
-        verify(mListener, after(75).times(3)).onReportLocation(any(LocationResult.class));
+        verify(mListener, after(1000).times(2)).onReportLocation(any(LocationResult.class));
     }
 
     @Test
     public void testThrottle_idleExit() {
-        ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
+        ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(1000).build();
 
         mProvider.getController().setRequest(request);
         verify(mDelegate).onSetRequest(request);
@@ -127,17 +139,16 @@
 
         mInjector.getDeviceStationaryHelper().setStationary(true);
         verify(mDelegate).onSetRequest(ProviderRequest.EMPTY_REQUEST);
-        verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
-        verify(mListener, timeout(75).times(3)).onReportLocation(any(LocationResult.class));
+        verify(mListener, timeout(1100).times(2)).onReportLocation(any(LocationResult.class));
 
         mInjector.getDeviceIdleHelper().setIdle(false);
         verify(mDelegate, times(2)).onSetRequest(request);
-        verify(mListener, after(75).times(3)).onReportLocation(any(LocationResult.class));
+        verify(mListener, after(1000).times(2)).onReportLocation(any(LocationResult.class));
     }
 
     @Test
     public void testThrottle_NoInitialLocation() {
-        ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
+        ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(1000).build();
 
         mProvider.getController().setRequest(request);
         verify(mDelegate).onSetRequest(request);
@@ -149,11 +160,11 @@
         mDelegateProvider.reportLocation(createLocationResult("test_provider", mRandom));
         verify(mListener, times(1)).onReportLocation(any(LocationResult.class));
         verify(mDelegate, times(1)).onSetRequest(ProviderRequest.EMPTY_REQUEST);
-        verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
+        verify(mListener, timeout(1100).times(2)).onReportLocation(any(LocationResult.class));
 
         mInjector.getDeviceStationaryHelper().setStationary(false);
         verify(mDelegate, times(2)).onSetRequest(request);
-        verify(mListener, after(75).times(2)).onReportLocation(any(LocationResult.class));
+        verify(mListener, after(1000).times(2)).onReportLocation(any(LocationResult.class));
     }
 
     @Test
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 4c638d6..bb3eb81 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -16,6 +16,13 @@
 <configuration description="Runs Frameworks Services Tests.">
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="apct-instrumentation" />
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push-file" key="SimpleServiceTestApp3.apk"
+                value="/data/local/tmp/cts/content/SimpleServiceTestApp3.apk" />
+    </target_preparer>
+
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="install-arg" value="-t" />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
index 8b6b7c2..1d6ed03 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -296,14 +296,6 @@
     }
 
     @Test
-    public void testToggleSplitScreen_legacy() {
-        setupWithRealContext();
-        mSystemActionPerformer.performSystemAction(
-                AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
-        verify(mMockStatusBarManagerInternal).toggleSplitScreen();
-    }
-
-    @Test
     public void testScreenshot_requestsFromScreenshotHelper_legacy() {
         setupWithMockContext();
         mSystemActionPerformer.performSystemAction(
diff --git a/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
index 10f4c05..e6a8dea 100644
--- a/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -26,7 +27,9 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
+import android.os.IBinder;
 import android.os.SystemClock;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
@@ -42,6 +45,8 @@
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Build/Install/Run:
@@ -69,6 +74,12 @@
     private static final int ACTION_STOPPKG = 8;
     private static final int ACTION_ALL = ACTION_START | ACTION_KILL | ACTION_WAIT | ACTION_STOPPKG;
 
+    private static final String LOCAL_APK_BASE_PATH = "/data/local/tmp/cts/content/";
+    private static final String TEST_PACKAGE3_APK = "SimpleServiceTestApp3.apk";
+    private static final String ACTION_SERVICE_WITH_DEP_PKG =
+            "com.android.servicestests.apps.simpleservicetestapp.ACTION_SERVICE_WITH_DEP_PKG";
+    private static final String EXTRA_TARGET_PACKAGE = "target_package";
+
     private Context mContext;
     private Instrumentation mInstrumentation;
     private int mTestPackage1Uid;
@@ -199,6 +210,83 @@
         return res;
     }
 
+    @Test
+    public void testServiceWithDepPkgStopped() throws Exception {
+        final CountDownLatch[] latchHolder = new CountDownLatch[1];
+        final ServiceConnection conn = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                latchHolder[0].countDown();
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                latchHolder[0].countDown();
+            }
+        };
+
+        final long timeout = 5_000;
+        final long shortTimeout = 2_000;
+        final Intent intent = new Intent(ACTION_SERVICE_WITH_DEP_PKG);
+        final String testPkg = TEST_PACKAGE2_NAME;
+        final String libPkg = TEST_PACKAGE3_NAME;
+        final String apkPath = LOCAL_APK_BASE_PATH + TEST_PACKAGE3_APK;
+        final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+
+        intent.setComponent(ComponentName.unflattenFromString(testPkg + "/" + TEST_SERVICE_NAME));
+        intent.putExtra(EXTRA_TARGET_PACKAGE, libPkg);
+        try {
+            executeShellCmd("am service-restart-backoff disable " + testPkg);
+
+            latchHolder[0] = new CountDownLatch(1);
+            assertTrue("Unable to bind to test service in " + testPkg,
+                    mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE));
+            assertTrue("Timed out to bind service in " + testPkg,
+                    latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
+
+            Thread.sleep(shortTimeout);
+            assertTrue(libPkg + " should be a dependency package of " + testPkg,
+                    isPackageDependency(testPkg, libPkg));
+
+            // Force-stop lib package, the test service should be restarted.
+            latchHolder[0] = new CountDownLatch(2);
+            am.forceStopPackage(libPkg);
+            assertTrue("Test service in didn't restart in " + testPkg,
+                    latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
+
+            Thread.sleep(shortTimeout);
+
+            // Re-install the lib package, the test service should be restarted.
+            latchHolder[0] = new CountDownLatch(2);
+            assertTrue("Unable to install package " + apkPath, installPackage(apkPath));
+            assertTrue("Test service in didn't restart in " + testPkg,
+                    latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
+
+            Thread.sleep(shortTimeout);
+
+            // Force-stop the service package, the test service should not be restarted.
+            latchHolder[0] = new CountDownLatch(2);
+            am.forceStopPackage(testPkg);
+            assertFalse("Test service should not be restarted in " + testPkg,
+                    latchHolder[0].await(timeout * 2, TimeUnit.MILLISECONDS));
+        } finally {
+            executeShellCmd("am service-restart-backoff enable " + testPkg);
+            mContext.unbindService(conn);
+            am.forceStopPackage(testPkg);
+        }
+    }
+
+    private boolean isPackageDependency(String pkgName, String libPackage) throws Exception {
+        final String output = SystemUtil.runShellCommand("dumpsys activity processes " + pkgName);
+        final Matcher matcher = Pattern.compile("packageDependencies=\\{.*?\\b"
+                + libPackage + "\\b.*?\\}").matcher(output);
+        return matcher.find();
+    }
+
+    private boolean installPackage(String apkPath) throws Exception {
+        return executeShellCmd("pm install -t " + apkPath).equals("Success\n");
+    }
+
     private void startServiceAndWait(String pkgName, MyUidImportanceListener uidListener,
             long timeout) throws Exception {
         final Intent intent = new Intent();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index b3f7587..b255a35 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -302,6 +302,65 @@
         testInvokesCancel(session -> session.onDialogDismissed(DISMISSED_REASON_NEGATIVE, null));
     }
 
+    // TODO (b/208484275) : Enable these tests
+    // @Test
+    // public void testPreAuth_canAuthAndPrivacyDisabled() throws Exception {
+    //     SensorPrivacyManager manager = ExtendedMockito.mock(SensorPrivacyManager.class);
+    //     when(manager
+    //             .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, anyInt()))
+    //             .thenReturn(false);
+    //     when(mContext.getSystemService(SensorPrivacyManager.class))
+    //             .thenReturn(manager);
+    //     setupFace(1 /* id */, false /* confirmationAlwaysRequired */,
+    //             mock(IBiometricAuthenticator.class));
+    //     final PromptInfo promptInfo = createPromptInfo(Authenticators.BIOMETRIC_STRONG);
+    //     final PreAuthInfo preAuthInfo = createPreAuthInfo(mSensors, 0, promptInfo, false);
+    //     assertEquals(BiometricManager.BIOMETRIC_SUCCESS, preAuthInfo.getCanAuthenticateResult());
+    //     for (BiometricSensor sensor : preAuthInfo.eligibleSensors) {
+    //         assertEquals(BiometricSensor.STATE_UNKNOWN, sensor.getSensorState());
+    //     }
+    // }
+
+    // @Test
+    // public void testPreAuth_cannotAuthAndPrivacyEnabled() throws Exception {
+    //     SensorPrivacyManager manager = ExtendedMockito.mock(SensorPrivacyManager.class);
+    //     when(manager
+    //             .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, anyInt()))
+    //             .thenReturn(true);
+    //     when(mContext.getSystemService(SensorPrivacyManager.class))
+    //             .thenReturn(manager);
+    //     setupFace(1 /* id */, false /* confirmationAlwaysRequired */,
+    //             mock(IBiometricAuthenticator.class));
+    //     final PromptInfo promptInfo = createPromptInfo(Authenticators.BIOMETRIC_STRONG);
+    //     final PreAuthInfo preAuthInfo = createPreAuthInfo(mSensors, 0, promptInfo, false);
+    //     assertEquals(BiometricManager.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED,
+    //             preAuthInfo.getCanAuthenticateResult());
+    //     // Even though canAuth returns privacy enabled, we should still be able to authenticate.
+    //     for (BiometricSensor sensor : preAuthInfo.eligibleSensors) {
+    //         assertEquals(BiometricSensor.STATE_UNKNOWN, sensor.getSensorState());
+    //     }
+    // }
+
+    // @Test
+    // public void testPreAuth_canAuthAndPrivacyEnabledCredentialEnabled() throws Exception {
+    //     SensorPrivacyManager manager = ExtendedMockito.mock(SensorPrivacyManager.class);
+    //     when(manager
+    //             .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, anyInt()))
+    //             .thenReturn(true);
+    //     when(mContext.getSystemService(SensorPrivacyManager.class))
+    //             .thenReturn(manager);
+    //     setupFace(1 /* id */, false /* confirmationAlwaysRequired */,
+    //             mock(IBiometricAuthenticator.class));
+    //     final PromptInfo promptInfo =
+    //             createPromptInfo(Authenticators.BIOMETRIC_STRONG
+    //             | Authenticators. DEVICE_CREDENTIAL);
+    //     final PreAuthInfo preAuthInfo = createPreAuthInfo(mSensors, 0, promptInfo, false);
+    //     assertEquals(BiometricManager.BIOMETRIC_SUCCESS, preAuthInfo.getCanAuthenticateResult());
+    //     for (BiometricSensor sensor : preAuthInfo.eligibleSensors) {
+    //         assertEquals(BiometricSensor.STATE_UNKNOWN, sensor.getSensorState());
+    //     }
+    // }
+
     private void testInvokesCancel(Consumer<AuthSession> sessionConsumer) throws RemoteException {
         final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
 
@@ -331,7 +390,8 @@
                 userId,
                 promptInfo,
                 TEST_PACKAGE,
-                checkDevicePolicyManager);
+                checkDevicePolicyManager,
+                mContext);
     }
 
     private AuthSession createAuthSession(List<BiometricSensor> sensors,
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
index 78afb7b..3cc105e 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
@@ -18,6 +18,7 @@
         package="com.android.servicestests.apps.simpleservicetestapp">
 
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
 
     <application>
         <service android:name=".SimpleService"
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
index 4e981b2..b8654d7 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
@@ -17,8 +17,10 @@
 
 import android.app.Service;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
@@ -33,6 +35,9 @@
     private static final String TEST_CLASS =
             "com.android.servicestests.apps.simpleservicetestapp.SimpleService";
 
+    private static final String ACTION_SERVICE_WITH_DEP_PKG =
+            "com.android.servicestests.apps.simpleservicetestapp.ACTION_SERVICE_WITH_DEP_PKG";
+
     private static final String EXTRA_CALLBACK = "callback";
     private static final String EXTRA_COMMAND = "command";
     private static final String EXTRA_FLAGS = "flags";
@@ -118,6 +123,21 @@
 
     @Override
     public IBinder onBind(Intent intent) {
+        if (ACTION_SERVICE_WITH_DEP_PKG.equals(intent.getAction())) {
+            final String targetPkg = intent.getStringExtra(EXTRA_TARGET_PACKAGE);
+            Log.i(TAG, "SimpleService.onBind: " + ACTION_SERVICE_WITH_DEP_PKG + " " + targetPkg);
+            if (targetPkg != null) {
+                Context pkgContext = null;
+                try {
+                    pkgContext = createPackageContext(targetPkg,
+                            Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.e(TAG, "Unable to create package context for " + pkgContext, e);
+                }
+                // This effectively loads the target package as a dependency.
+                pkgContext.getClassLoader();
+            }
+        }
         return mBinder;
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 0ad119d..c994b41 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -61,10 +61,6 @@
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 
-import static com.android.server.notification.NotificationManagerService.ACTION_DISABLE_NAS;
-import static com.android.server.notification.NotificationManagerService.ACTION_ENABLE_NAS;
-import static com.android.server.notification.NotificationManagerService.ACTION_LEARNMORE_NAS;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.Assert.assertEquals;
@@ -325,7 +321,6 @@
     @Mock
     MultiRateLimiter mToastRateLimiter;
     BroadcastReceiver mPackageIntentReceiver;
-    BroadcastReceiver mNASIntentReceiver;
     NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
     private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
             1 << 30);
@@ -553,14 +548,9 @@
                     && filter.hasAction(Intent.ACTION_PACKAGES_UNSUSPENDED)
                     && filter.hasAction(Intent.ACTION_PACKAGES_SUSPENDED)) {
                 mPackageIntentReceiver = broadcastReceivers.get(i);
-            } else if (filter.hasAction(ACTION_ENABLE_NAS)
-                    && filter.hasAction(ACTION_DISABLE_NAS)
-                    && filter.hasAction(ACTION_LEARNMORE_NAS)) {
-                mNASIntentReceiver = broadcastReceivers.get(i);
             }
         }
         assertNotNull("package intent receiver should exist", mPackageIntentReceiver);
-        assertNotNull("nas intent receiver should exist", mNASIntentReceiver);
 
         // Pretend the shortcut exists
         List<ShortcutInfo> shortcutInfos = new ArrayList<>();
@@ -655,16 +645,6 @@
         mPackageIntentReceiver.onReceive(getContext(), intent);
     }
 
-    private void simulateNASUpgradeBroadcast(String action, int uid) {
-        final Bundle extras = new Bundle();
-        extras.putInt(Intent.EXTRA_USER_ID, uid);
-
-        final Intent intent = new Intent(action);
-        intent.putExtras(extras);
-
-        mNASIntentReceiver.onReceive(getContext(), intent);
-    }
-
     private ArrayMap<Boolean, ArrayList<ComponentName>> generateResetComponentValues() {
         ArrayMap<Boolean, ArrayList<ComponentName>> changed = new ArrayMap<>();
         changed.put(true, new ArrayList<>());
@@ -6042,7 +6022,7 @@
     }
 
     @Test
-    public void testNASSettingUpgrade_userSetNull_noOnBoarding() throws RemoteException {
+    public void testNASSettingUpgrade_userSetNull() throws RemoteException {
         ComponentName newDefaultComponent = ComponentName.unflattenFromString("package/Component1");
         TestableNotificationManagerService service = spy(mService);
         int userId = 11;
@@ -6055,14 +6035,13 @@
                 .thenReturn(new ArrayList<>());
         when(mAssistants.hasUserSet(userId)).thenReturn(true);
 
-        service.migrateDefaultNASShowNotificationIfNecessary();
+        service.migrateDefaultNAS();
         assertTrue(service.isNASMigrationDone(userId));
-        verify(service, times(0)).createNASUpgradeNotification(eq(userId));
         verify(mAssistants, times(1)).clearDefaults();
     }
 
     @Test
-    public void testNASSettingUpgrade_userSetSameDefault_noOnBoarding() throws RemoteException {
+    public void testNASSettingUpgrade_userSet() throws RemoteException {
         ComponentName defaultComponent = ComponentName.unflattenFromString("package/Component1");
         TestableNotificationManagerService service = spy(mService);
         int userId = 11;
@@ -6075,55 +6054,10 @@
                 .thenReturn(new ArrayList(Arrays.asList(defaultComponent)));
         when(mAssistants.hasUserSet(userId)).thenReturn(true);
 
-        service.migrateDefaultNASShowNotificationIfNecessary();
-        assertTrue(service.isNASMigrationDone(userId));
-        verify(service, times(0)).createNASUpgradeNotification(eq(userId));
-        verify(mAssistants, times(1)).resetDefaultFromConfig();
-    }
-
-    @Test
-    public void testNASSettingUpgrade_userSetDifferentDefault_showOnboarding()
-            throws RemoteException {
-        ComponentName oldDefaultComponent = ComponentName.unflattenFromString("package/Component1");
-        ComponentName newDefaultComponent = ComponentName.unflattenFromString("package/Component2");
-        TestableNotificationManagerService service = spy(mService);
-        int userId = 11;
-        setUsers(new int[]{userId});
-        setNASMigrationDone(false, userId);
-        when(mAssistants.getDefaultComponents())
-                .thenReturn(new ArraySet<>(Arrays.asList(oldDefaultComponent)));
-        when(mAssistants.getDefaultFromConfig())
-                .thenReturn(newDefaultComponent);
-        when(mAssistants.getAllowedComponents(anyInt()))
-                .thenReturn(Arrays.asList(oldDefaultComponent));
-        when(mAssistants.hasUserSet(userId)).thenReturn(true);
-
-        service.migrateDefaultNASShowNotificationIfNecessary();
-        assertFalse(service.isNASMigrationDone(userId));
-        //TODO(b/192450820)
-        //verify(service, times(1)).createNASUpgradeNotification(eq(userId));
-        verify(mAssistants, times(0)).resetDefaultFromConfig();
-
-        //Test user clear data before enable/disable from onboarding notification
-        ArrayMap<Boolean, ArrayList<ComponentName>> changedListeners =
-                generateResetComponentValues();
-        when(mListeners.resetComponents(anyString(), anyInt())).thenReturn(changedListeners);
-        ArrayMap<Boolean, ArrayList<ComponentName>> changes = new ArrayMap<>();
-        changes.put(true, new ArrayList(Arrays.asList(newDefaultComponent)));
-        changes.put(false, new ArrayList());
-        when(mAssistants.resetComponents(anyString(), anyInt())).thenReturn(changes);
-
-        //Clear data
-        service.getBinderService().clearData("package", userId, false);
-        //Test migrate flow again
-        service.migrateDefaultNASShowNotificationIfNecessary();
-
-        //The notification should be still there
-        assertFalse(service.isNASMigrationDone(userId));
-        //TODO(b/192450820)
-        //verify(service, times(2)).createNASUpgradeNotification(eq(userId));
-        verify(mAssistants, times(0)).resetDefaultFromConfig();
-        assertEquals(oldDefaultComponent, service.getApprovedAssistant(userId));
+        service.migrateDefaultNAS();
+        verify(mAssistants, times(1)).setUserSet(userId, false);
+        //resetDefaultAssistantsIfNecessary should invoke from readPolicyXml() and migration
+        verify(mAssistants, times(2)).resetDefaultAssistantsIfNecessary();
     }
 
     @Test
@@ -6143,24 +6077,22 @@
                 .thenReturn(new ArraySet<>(Arrays.asList(oldDefaultComponent)));
         when(mAssistants.getDefaultFromConfig())
                 .thenReturn(newDefaultComponent);
-        //User1: need onboarding
+        //User1: set different NAS
         when(mAssistants.getAllowedComponents(userId1))
                 .thenReturn(Arrays.asList(oldDefaultComponent));
-        //User2: no onboarding
+        //User2: set to none
         when(mAssistants.getAllowedComponents(userId2))
-                .thenReturn(Arrays.asList(newDefaultComponent));
+                .thenReturn(new ArrayList<>());
 
         when(mAssistants.hasUserSet(userId1)).thenReturn(true);
         when(mAssistants.hasUserSet(userId2)).thenReturn(true);
 
-        service.migrateDefaultNASShowNotificationIfNecessary();
-        assertFalse(service.isNASMigrationDone(userId1));
+        service.migrateDefaultNAS();
+        // user1's setting get reset
+        verify(mAssistants, times(1)).setUserSet(userId1, false);
+        verify(mAssistants, times(0)).setUserSet(eq(userId2), anyBoolean());
         assertTrue(service.isNASMigrationDone(userId2));
 
-        //TODO(b/192450820)
-        //verify(service, times(1)).createNASUpgradeNotification(any(Integer.class));
-        // only user2's default get updated
-        verify(mAssistants, times(1)).resetDefaultFromConfig();
     }
 
     @Test
@@ -6180,7 +6112,7 @@
                 .thenReturn(new ArraySet<>(Arrays.asList(oldDefaultComponent)));
         when(mAssistants.getDefaultFromConfig())
                 .thenReturn(newDefaultComponent);
-        //Both profiles: need onboarding
+        //Both profiles: set different NAS
         when(mAssistants.getAllowedComponents(userId1))
                 .thenReturn(Arrays.asList(oldDefaultComponent));
         when(mAssistants.getAllowedComponents(userId2))
@@ -6189,13 +6121,9 @@
         when(mAssistants.hasUserSet(userId1)).thenReturn(true);
         when(mAssistants.hasUserSet(userId2)).thenReturn(true);
 
-        service.migrateDefaultNASShowNotificationIfNecessary();
+        service.migrateDefaultNAS();
         assertFalse(service.isNASMigrationDone(userId1));
         assertFalse(service.isNASMigrationDone(userId2));
-
-        // TODO(b/192450820): only user1 get notification
-        //verify(service, times(1)).createNASUpgradeNotification(eq(userId1));
-        //verify(service, times(0)).createNASUpgradeNotification(eq(userId2));
     }
 
 
@@ -6223,79 +6151,16 @@
         //Clear data
         service.getBinderService().clearData("package", userId, false);
         //Test migrate flow again
-        service.migrateDefaultNASShowNotificationIfNecessary();
+        service.migrateDefaultNAS();
 
-        //TODO(b/192450820): The notification should not appear again
-        //verify(service, times(0)).createNASUpgradeNotification(eq(userId));
-        verify(mAssistants, times(0)).resetDefaultFromConfig();
+        //Migration should not happen again
+        verify(mAssistants, times(0)).setUserSet(userId, false);
+        verify(mAssistants, times(0)).clearDefaults();
+        //resetDefaultAssistantsIfNecessary should only invoke once from readPolicyXml()
+        verify(mAssistants, times(1)).resetDefaultAssistantsIfNecessary();
+
     }
 
-    @Test
-    public void testNASUpgradeNotificationDisableBroadcast_multiProfile() {
-        int userId1 = 11;
-        int userId2 = 12;
-        setUsers(new int[]{userId1, userId2});
-        when(mUm.isManagedProfile(userId2)).thenReturn(true);
-        when(mUm.getProfileIds(userId1, false)).thenReturn(new int[]{userId1, userId2});
-
-        TestableNotificationManagerService service = spy(mService);
-        setNASMigrationDone(false, userId1);
-        setNASMigrationDone(false, userId2);
-
-        simulateNASUpgradeBroadcast(ACTION_DISABLE_NAS, userId1);
-
-        assertTrue(service.isNASMigrationDone(userId1));
-        assertTrue(service.isNASMigrationDone(userId2));
-        // User disabled the NAS from notification, the default stored in xml should be null
-        // rather than the new default
-        verify(mAssistants, times(1)).clearDefaults();
-        verify(mAssistants, times(0)).resetDefaultFromConfig();
-
-        //TODO(b/192450820):No more notification after disabled
-        //service.migrateDefaultNASShowNotificationIfNecessary();
-        //verify(service, times(0)).createNASUpgradeNotification(anyInt());
-    }
-
-    @Test
-    public void testNASUpgradeNotificationEnableBroadcast_multiUser() {
-        int userId1 = 11;
-        int userId2 = 12;
-        setUsers(new int[]{userId1, userId2});
-        when(mUm.getProfileIds(userId1, false)).thenReturn(new int[]{userId1});
-
-        TestableNotificationManagerService service = spy(mService);
-        setNASMigrationDone(false, userId1);
-        setNASMigrationDone(false, userId2);
-
-        simulateNASUpgradeBroadcast(ACTION_ENABLE_NAS, userId1);
-
-        assertTrue(service.isNASMigrationDone(userId1));
-        assertFalse(service.isNASMigrationDone(userId2));
-        verify(mAssistants, times(1)).resetDefaultFromConfig();
-
-        //TODO(b/192450820)
-        //service.migrateDefaultNASShowNotificationIfNecessary();
-        //verify(service, times(0)).createNASUpgradeNotification(eq(userId1));
-    }
-
-    @Test
-    public void testNASUpgradeNotificationLearnMoreBroadcast() {
-        int userId = 11;
-        setUsers(new int[]{userId});
-        TestableNotificationManagerService service = spy(mService);
-        setNASMigrationDone(false, userId);
-        doNothing().when(mContext).startActivity(any());
-
-        simulateNASUpgradeBroadcast(ACTION_LEARNMORE_NAS, userId);
-
-        verify(mContext, times(1)).startActivity(any(Intent.class));
-        assertFalse(service.isNASMigrationDone(userId));
-        //TODO(b/192450820)
-        //verify(service, times(0)).createNASUpgradeNotification(eq(userId));
-        verify(mAssistants, times(0)).resetDefaultFromConfig();
-    }
-
-
     private void setNASMigrationDone(boolean done, int userId) {
         Settings.Secure.putIntForUser(mContext.getContentResolver(),
                 Settings.Secure.NAS_SETTINGS_UPDATED, done ? 1 : 0, userId);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index c0959d3..2ea7fda 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -22,6 +22,9 @@
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -92,6 +95,7 @@
         final ActivityRecord activity = createActivityRecord(dc);
 
         mDc.prepareAppTransition(TRANSIT_OPEN);
+        mDc.prepareAppTransition(TRANSIT_KEYGUARD_OCCLUDE);
         mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY);
         mDc.mOpeningApps.add(activity);
         assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
@@ -102,6 +106,22 @@
     }
 
     @Test
+    public void testKeyguardUnoccludeOcclude() {
+        final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+        final ActivityRecord activity = createActivityRecord(dc);
+
+        mDc.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE);
+        mDc.prepareAppTransition(TRANSIT_KEYGUARD_OCCLUDE);
+        mDc.mOpeningApps.add(activity);
+        assertEquals(TRANSIT_NONE,
+                AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+                        null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
+
+    }
+
+    @Test
     public void testKeyguardKeep() {
         final DisplayContent dc = createNewDisplay(Display.STATE_ON);
         final ActivityRecord activity = createActivityRecord(dc);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index 004e45a..b2b844b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -180,23 +180,23 @@
     }
 
     private int getNonDecorDisplayWidth(DisplayInfo di) {
-        return mDisplayPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight,
-                di.rotation, 0 /* ui */, di.displayCutout);
+        return mDisplayPolicy.getNonDecorDisplaySize(di.logicalWidth, di.logicalHeight,
+                di.rotation, 0 /* ui */, di.displayCutout).x;
     }
 
     private int getNonDecorDisplayHeight(DisplayInfo di) {
-        return mDisplayPolicy.getNonDecorDisplayHeight(di.logicalWidth, di.logicalHeight,
-                di.rotation, 0 /* ui */, di.displayCutout);
+        return mDisplayPolicy.getNonDecorDisplaySize(di.logicalWidth, di.logicalHeight,
+                di.rotation, 0 /* ui */, di.displayCutout).y;
     }
 
     private int getConfigDisplayWidth(DisplayInfo di) {
-        return mDisplayPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight,
-                di.rotation, 0 /* ui */, di.displayCutout);
+        return mDisplayPolicy.getConfigDisplaySize(di.logicalWidth, di.logicalHeight,
+                di.rotation, 0 /* ui */, di.displayCutout).x;
     }
 
     private int getConfigDisplayHeight(DisplayInfo di) {
-        return mDisplayPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight,
-                di.rotation, 0 /* ui */, di.displayCutout);
+        return mDisplayPolicy.getConfigDisplaySize(di.logicalWidth, di.logicalHeight,
+                di.rotation, 0 /* ui */, di.displayCutout).y;
     }
 
     private static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 8c1045d..bfaa815 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -899,22 +899,21 @@
 
     /**
      * Test that root activity index is reported correctly when looking for the 'effective root' in
-     * case when bottom activity is finishing. Ignore the relinquishing task identity if it's not a
-     * system activity even with the FLAG_RELINQUISH_TASK_IDENTITY.
+     * case when bottom activities are relinquishing task identity or finishing.
      */
     @Test
     public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() {
-        final Task task = getTestTask();
+        final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final Task task = activity0.getTask();
         // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and
         // one above as finishing.
-        final ActivityRecord activity0 = task.getBottomMostActivity();
         activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
         activity1.finishing = true;
         new ActivityBuilder(mAtm).setTask(task).build();
 
         assertEquals("The first non-finishing activity and non-relinquishing task identity "
-                + "must be reported.", task.getChildAt(0), task.getRootActivity(
+                + "must be reported.", task.getChildAt(2), task.getRootActivity(
                 false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
     }
 
@@ -934,21 +933,21 @@
     }
 
     /**
-     * Test that the root activity index is reported correctly when looking for the
-     * 'effective root' for the case when all non-system activities have relinquishTaskIdentity set.
+     * Test that the topmost activity index is reported correctly when looking for the
+     * 'effective root' for the case when all activities have relinquishTaskIdentity set.
      */
     @Test
     public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() {
-        final Task task = getTestTask();
+        final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final Task task = activity0.getTask();
         // Set relinquishTaskIdentity for all activities in the task
-        final ActivityRecord activity0 = task.getBottomMostActivity();
         activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
         activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
 
-        assertEquals("The topmost activity in the task must be reported.", task.getChildAt(0),
-                task.getRootActivity(false /*ignoreRelinquishIdentity*/,
-                        true /*setToBottomIfNone*/));
+        assertEquals("The topmost activity in the task must be reported.",
+                task.getChildAt(task.getChildCount() - 1), task.getRootActivity(
+                        false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
     }
 
     /** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */
@@ -1086,14 +1085,14 @@
     }
 
     /**
-     * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with non-system
-     * activity that relinquishes task identity.
+     * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that
+     * relinquishes task identity.
      */
     @Test
     public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() {
-        final Task task = getTestTask();
+        final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final Task task = activity0.getTask();
         // Make the current root activity relinquish task identity
-        final ActivityRecord activity0 = task.getBottomMostActivity();
         activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
         // Add an extra activity on top - this will be the new root
         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
@@ -1102,7 +1101,7 @@
 
         assertEquals(task.mTaskId,
                 ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */));
-        assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
+        assertEquals(task.mTaskId,
                 ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */));
         assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
                 ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */));
@@ -1189,6 +1188,46 @@
         verify(task).setIntent(eq(activity0));
     }
 
+    /**
+     * Test {@link Task#updateEffectiveIntent()} when activity with relinquishTaskIdentity but
+     * another with different uid. This should make the task use the root activity when updating the
+     * intent.
+     */
+    @Test
+    public void testUpdateEffectiveIntent_relinquishingWithDifferentUid() {
+        final ActivityRecord activity0 = new ActivityBuilder(mAtm)
+                .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build();
+        final Task task = activity0.getTask();
+
+        // Add an extra activity on top
+        new ActivityBuilder(mAtm).setUid(11).setTask(task).build();
+
+        spyOn(task);
+        task.updateEffectiveIntent();
+        verify(task).setIntent(eq(activity0));
+    }
+
+    /**
+     * Test {@link Task#updateEffectiveIntent()} with activities set as relinquishTaskIdentity.
+     * This should make the task use the topmost activity when updating the intent.
+     */
+    @Test
+    public void testUpdateEffectiveIntent_relinquishingMultipleActivities() {
+        final ActivityRecord activity0 = new ActivityBuilder(mAtm)
+                .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build();
+        final Task task = activity0.getTask();
+        // Add an extra activity on top
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+        activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+
+        // Add an extra activity on top
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
+
+        spyOn(task);
+        task.updateEffectiveIntent();
+        verify(task).setIntent(eq(activity2));
+    }
+
     @Test
     public void testSaveLaunchingStateWhenConfigurationChanged() {
         LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 9c9e41b..b8a14b8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5794,6 +5794,7 @@
         sDefaults.putInt(
                 KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
                 120000);
+        sDefaults.putAll(ImsServiceEntitlement.getDefaults());
         sDefaults.putAll(Gps.getDefaults());
         sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
                 new int[] {