Merge "Adding empty locale-config case" into tm-dev
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e9df50f..961135f 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1207,11 +1207,6 @@
      */
     public void sendTrackballEventSync(MotionEvent event) {
         validateNotAppThread();
-        if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
-            throw new IllegalArgumentException(
-                    "Cannot inject pointer events from sendTrackballEventSync()."
-                            + " Use sendPointerSync() to inject pointer events.");
-        }
         if (!event.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
             event.setSource(InputDevice.SOURCE_TRACKBALL);
         }
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 5df64e3..d94ad3a 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -623,6 +623,11 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        if (!mPhysicalCameraSettings.containsKey(mLogicalCameraId)) {
+            throw new IllegalStateException("Physical camera settings map must contain a key for "
+                    + "the logical camera id.");
+        }
+
         int physicalCameraCount = mPhysicalCameraSettings.size();
         dest.writeInt(physicalCameraCount);
         //Logical camera id and settings always come first.
diff --git a/core/java/android/hardware/input/InputDeviceSensorManager.java b/core/java/android/hardware/input/InputDeviceSensorManager.java
index 89db857..8a40d00 100644
--- a/core/java/android/hardware/input/InputDeviceSensorManager.java
+++ b/core/java/android/hardware/input/InputDeviceSensorManager.java
@@ -98,7 +98,7 @@
      */
     private void updateInputDeviceSensorInfoLocked(int deviceId) {
         final InputDevice inputDevice = InputDevice.getDevice(deviceId);
-        if (inputDevice.hasSensor()) {
+        if (inputDevice != null && inputDevice.hasSensor()) {
             final InputSensorInfo[] sensorInfos =
                     mInputManager.getSensorList(deviceId);
             populateSensorsForInputDeviceLocked(deviceId, sensorInfos);
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 03d1151..bd6c4e1 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -151,6 +151,8 @@
 
         private boolean mDrawLegacyNavigationBarBackground;
 
+        private final Rect mTempRect = new Rect();
+
         Impl(@NonNull InputMethodService inputMethodService) {
             mService = inputMethodService;
         }
@@ -281,13 +283,12 @@
                         touchableRegion.set(originalInsets.touchableRegion);
                         break;
                 }
-                final Rect navBarRect = new Rect(decor.getLeft(),
-                        decor.getBottom() - systemInsets.bottom,
+                mTempRect.set(decor.getLeft(), decor.getBottom() - systemInsets.bottom,
                         decor.getRight(), decor.getBottom());
                 if (touchableRegion == null) {
-                    touchableRegion = new Region(navBarRect);
+                    touchableRegion = new Region(mTempRect);
                 } else {
-                    touchableRegion.union(navBarRect);
+                    touchableRegion.union(mTempRect);
                 }
 
                 dest.touchableRegion.set(touchableRegion);
diff --git a/core/java/android/net/TEST_MAPPING b/core/java/android/net/TEST_MAPPING
index a379c33..3df5616 100644
--- a/core/java/android/net/TEST_MAPPING
+++ b/core/java/android/net/TEST_MAPPING
@@ -17,7 +17,7 @@
       "path": "frameworks/opt/net/wifi"
     }
   ],
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "FrameworksCoreTests",
       "options": [
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ee1e80d..c45a4c7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2767,17 +2767,16 @@
             dispatchApplyInsets(host);
         }
 
+        if (mFirst) {
+            // make sure touch mode code executes by setting cached value
+            // to opposite of the added touch mode.
+            mAttachInfo.mInTouchMode = !mAddedTouchMode;
+            ensureTouchModeLocally(mAddedTouchMode);
+        }
+
         boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
         if (layoutRequested) {
-
-            final Resources res = mView.getContext().getResources();
-
-            if (mFirst) {
-                // make sure touch mode code executes by setting cached value
-                // to opposite of the added touch mode.
-                mAttachInfo.mInTouchMode = !mAddedTouchMode;
-                ensureTouchModeLocally(mAddedTouchMode);
-            } else {
+            if (!mFirst) {
                 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                     windowSizeMayChange = true;
@@ -2797,7 +2796,7 @@
             }
 
             // Ask host how big it wants to be
-            windowSizeMayChange |= measureHierarchy(host, lp, res,
+            windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),
                     desiredWindowWidth, desiredWindowHeight);
         }
 
@@ -6407,6 +6406,24 @@
                 return FINISH_HANDLED;
             }
 
+            // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the
+            // view tree and invoke the appropriate {@link OnBackInvokedCallback}.
+            if (isBack(event)
+                    && mContext != null
+                    && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
+                OnBackInvokedCallback topCallback =
+                        getOnBackInvokedDispatcher().getTopCallback();
+                if (event.getAction() == KeyEvent.ACTION_UP) {
+                    if (topCallback != null) {
+                        topCallback.onBackInvoked();
+                        return FINISH_HANDLED;
+                    }
+                } else {
+                    // Drop other actions such as {@link KeyEvent.ACTION_DOWN}.
+                    return FINISH_NOT_HANDLED;
+                }
+            }
+
             // Deliver the key to the view hierarchy.
             if (mView.dispatchKeyEvent(event)) {
                 return FINISH_HANDLED;
@@ -6416,19 +6433,6 @@
                 return FINISH_NOT_HANDLED;
             }
 
-            if (isBack(event)
-                    && mContext != null
-                    && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
-                // Invoke the appropriate {@link OnBackInvokedCallback} if the new back
-                // navigation should be used, and the key event is not handled by anything else.
-                OnBackInvokedCallback topCallback =
-                        getOnBackInvokedDispatcher().getTopCallback();
-                if (topCallback != null) {
-                    topCallback.onBackInvoked();
-                    return FINISH_HANDLED;
-                }
-            }
-
             // This dispatch is for windows that don't have a Window.Callback. Otherwise,
             // the Window.Callback usually will have already called this (see
             // DecorView.superDispatchKeyEvent) leaving this call a no-op.
@@ -10766,11 +10770,7 @@
                 KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
                 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                 InputDevice.SOURCE_KEYBOARD);
-
-        ev.setDisplayId(mContext.getDisplay().getDisplayId());
-        if (mView != null) {
-            mView.dispatchKeyEvent(ev);
-        }
+        enqueueInputEvent(ev);
     }
 
     private void registerCompatOnBackInvokedCallback() {
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 5007df5..4b9a957 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -41,14 +41,13 @@
  * @hide
  */
 public class WindowContextController {
-    // TODO(220049234): Disable attach debug logging before shipping.
-    private static final boolean DEBUG_ATTACH = true;
+    private static final boolean DEBUG_ATTACH = false;
     private static final String TAG = "WindowContextController";
 
     /**
-     * {@link AttachStatus.STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a
+     * {@link AttachStatus#STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a
      * {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a
-     * WindowToken after this flag sets to {@link AttachStatus.STATUS_ATTACHED}.
+     * WindowToken after this flag sets to {@link AttachStatus#STATUS_ATTACHED}.
      */
     @VisibleForTesting
     public int mAttachedToDisplayArea = AttachStatus.STATUS_INITIALIZED;
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index bea2c78..cad8b9b 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -51,9 +51,10 @@
     private IWindowSession mWindowSession;
     private IWindow mWindow;
     private static final String TAG = "WindowOnBackDispatcher";
-    private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
-    private static final boolean IS_BACK_PREDICTABILITY_ENABLED = SystemProperties
-            .getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
+    private static final boolean ENABLE_PREDICTIVE_BACK = SystemProperties
+            .getInt("persist.wm.debug.predictive_back", 1) != 0;
+    private static final boolean ALWAYS_ENFORCE_PREDICTIVE_BACK = SystemProperties
+            .getInt("persist.wm.debug.predictive_back_always_enforce", 0) != 0;
 
     /** Convenience hashmap to quickly decide if a callback has been added. */
     private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
@@ -254,18 +255,18 @@
     public static boolean isOnBackInvokedCallbackEnabled(@Nullable Context context) {
         // new back is enabled if the feature flag is enabled AND the app does not explicitly
         // request legacy back.
-        boolean featureFlagEnabled = IS_BACK_PREDICTABILITY_ENABLED;
+        boolean featureFlagEnabled = ENABLE_PREDICTIVE_BACK;
         // If the context is null, we assume true and fallback on the two other conditions.
         boolean appRequestsPredictiveBack =
                 context != null && context.getApplicationInfo().isOnBackInvokedCallbackEnabled();
 
         if (DEBUG) {
             Log.d(TAG, TextUtils.formatSimple("App: %s featureFlagEnabled=%s "
-                            + "appRequestsPredictiveBack=%s",
+                            + "appRequestsPredictiveBack=%s alwaysEnforce=%s",
                     context != null ? context.getApplicationInfo().packageName : "null context",
-                    featureFlagEnabled, appRequestsPredictiveBack));
+                    featureFlagEnabled, appRequestsPredictiveBack, ALWAYS_ENFORCE_PREDICTIVE_BACK));
         }
 
-        return featureFlagEnabled && appRequestsPredictiveBack;
+        return featureFlagEnabled && (appRequestsPredictiveBack || ALWAYS_ENFORCE_PREDICTIVE_BACK);
     }
 }
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 1feb5d4..06d12b5 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -46,6 +46,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.build.UnboundedSdkLevel;
 
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
@@ -58,7 +59,6 @@
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -126,7 +126,7 @@
          *
          * <p>0 means not specified.
          */
-        public final int onBootclasspathSince;
+        public final String onBootclasspathSince;
 
         /**
          * SDK version this library was removed from the BOOTCLASSPATH.
@@ -138,7 +138,7 @@
          *
          * <p>0 means not specified.
          */
-        public final int onBootclasspathBefore;
+        public final String onBootclasspathBefore;
 
         /**
          * Declares whether this library can be safely ignored from <uses-library> tags.
@@ -155,19 +155,19 @@
         @VisibleForTesting
         public SharedLibraryEntry(String name, String filename, String[] dependencies,
                 boolean isNative) {
-            this(name, filename, dependencies, 0 /* onBootclasspathSince */,
-                    0 /* onBootclasspathBefore */, isNative);
+            this(name, filename, dependencies, null /* onBootclasspathSince */,
+                    null /* onBootclasspathBefore */, isNative);
         }
 
         @VisibleForTesting
         public SharedLibraryEntry(String name, String filename, String[] dependencies,
-                int onBootclasspathSince, int onBootclassPathBefore) {
-            this(name, filename, dependencies, onBootclasspathSince, onBootclassPathBefore,
+                String onBootclasspathSince, String onBootclasspathBefore) {
+            this(name, filename, dependencies, onBootclasspathSince, onBootclasspathBefore,
                     false /* isNative */);
         }
 
         SharedLibraryEntry(String name, String filename, String[] dependencies,
-                int onBootclasspathSince, int onBootclasspathBefore, boolean isNative) {
+                String onBootclasspathSince, String onBootclasspathBefore, boolean isNative) {
             this.name = name;
             this.filename = filename;
             this.dependencies = dependencies;
@@ -175,16 +175,14 @@
             this.onBootclasspathBefore = onBootclasspathBefore;
             this.isNative = isNative;
 
-            canBeSafelyIgnored = this.onBootclasspathSince != 0
-                    && isSdkAtLeast(this.onBootclasspathSince);
-        }
-
-        private static boolean isSdkAtLeast(int level) {
-            if ("REL".equals(Build.VERSION.CODENAME)) {
-                return Build.VERSION.SDK_INT >= level;
-            }
-            return level == Build.VERSION_CODES.CUR_DEVELOPMENT
-                    || Build.VERSION.SDK_INT >= level;
+            // this entry can be ignored if either:
+            // - onBootclasspathSince is set and we are at or past that SDK
+            // - onBootclasspathBefore is set and we are before that SDK
+            canBeSafelyIgnored =
+                    (this.onBootclasspathSince != null
+                            && UnboundedSdkLevel.isAtLeast(this.onBootclasspathSince))
+                            || (this.onBootclasspathBefore != null
+                            && !UnboundedSdkLevel.isAtLeast(this.onBootclasspathBefore));
         }
     }
 
@@ -878,10 +876,8 @@
                             String lname = parser.getAttributeValue(null, "name");
                             String lfile = parser.getAttributeValue(null, "file");
                             String ldependency = parser.getAttributeValue(null, "dependency");
-                            int minDeviceSdk = XmlUtils.readIntAttribute(parser, "min-device-sdk",
-                                    0);
-                            int maxDeviceSdk = XmlUtils.readIntAttribute(parser, "max-device-sdk",
-                                    0);
+                            String minDeviceSdk = parser.getAttributeValue(null, "min-device-sdk");
+                            String maxDeviceSdk = parser.getAttributeValue(null, "max-device-sdk");
                             if (lname == null) {
                                 Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
                                         + parser.getPositionDescription());
@@ -889,15 +885,18 @@
                                 Slog.w(TAG, "<" + name + "> without file in " + permFile + " at "
                                         + parser.getPositionDescription());
                             } else {
-                                boolean allowedMinSdk = minDeviceSdk <= Build.VERSION.SDK_INT;
+                                boolean allowedMinSdk =
+                                        minDeviceSdk == null || UnboundedSdkLevel.isAtLeast(
+                                                minDeviceSdk);
                                 boolean allowedMaxSdk =
-                                        maxDeviceSdk == 0 || maxDeviceSdk >= Build.VERSION.SDK_INT;
+                                        maxDeviceSdk == null || UnboundedSdkLevel.isAtMost(
+                                                maxDeviceSdk);
                                 final boolean exists = new File(lfile).exists();
                                 if (allowedMinSdk && allowedMaxSdk && exists) {
-                                    int bcpSince = XmlUtils.readIntAttribute(parser,
-                                            "on-bootclasspath-since", 0);
-                                    int bcpBefore = XmlUtils.readIntAttribute(parser,
-                                            "on-bootclasspath-before", 0);
+                                    String bcpSince = parser.getAttributeValue(null,
+                                            "on-bootclasspath-since");
+                                    String bcpBefore = parser.getAttributeValue(null,
+                                            "on-bootclasspath-before");
                                     SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
                                             ldependency == null
                                                     ? new String[0] : ldependency.split(":"),
@@ -1487,7 +1486,7 @@
             addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0);
         }
 
-        if (isFilesystemSupported("erofs")) {
+        if (isErofsSupported()) {
             if (isKernelVersionAtLeast(5, 10)) {
                 addFeature(PackageManager.FEATURE_EROFS, 0);
             } else if (isKernelVersionAtLeast(4, 19)) {
@@ -1865,11 +1864,10 @@
         return Process.myUid() == Process.SYSTEM_UID;
     }
 
-    private static boolean isFilesystemSupported(String fs) {
+    private static boolean isErofsSupported() {
         try {
-            final byte[] fsTableData = Files.readAllBytes(Paths.get("/proc/filesystems"));
-            final String fsTable = new String(fsTableData, StandardCharsets.UTF_8);
-            return fsTable.contains("\t" + fs + "\n");
+            final Path path = Paths.get("/sys/fs/erofs");
+            return Files.exists(path);
         } catch (Exception e) {
             return false;
         }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 96fe7e1..4075c5f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2086,7 +2086,7 @@
 
     <!-- @SystemApi @hide Allows applications to register network factory or agent -->
     <permission android:name="android.permission.NETWORK_FACTORY"
-                android:protectionLevel="signature" />
+                android:protectionLevel="signature|role" />
 
     <!-- @SystemApi @hide Allows applications to access network stats provider -->
     <permission android:name="android.permission.NETWORK_STATS_PROVIDER"
@@ -2275,13 +2275,13 @@
          @hide
     -->
     <permission android:name="android.permission.BLUETOOTH_MAP"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- Allows bluetooth stack to access files
          @hide This should only be used by Bluetooth apk.
     -->
     <permission android:name="android.permission.BLUETOOTH_STACK"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- Allows uhid write access for creating virtual input devices
          @hide
@@ -2552,7 +2552,7 @@
     <!-- Allows access to configure network interfaces, configure/use IPSec, etc.
          @hide -->
     <permission android:name="android.permission.NET_ADMIN"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- Allows registration for remote audio playback. @hide -->
     <permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK"
@@ -2676,7 +2676,7 @@
     <!-- Allows listen permission to always reported system signal strength.
          @hide Used internally. -->
     <permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- @SystemApi Protects the ability to register any PhoneAccount with
          PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. This capability indicates that the PhoneAccount
@@ -3911,7 +3911,7 @@
          Not for use by third party apps.
          @hide -->
     <permission android:name="android.permission.MANAGE_APP_OPS_MODES"
-        android:protectionLevel="signature|installer|verifier" />
+        android:protectionLevel="signature|installer|verifier|role" />
 
     <!-- @SystemApi Allows an application to open windows that are for use by parts
          of the system user interface.
@@ -4792,7 +4792,7 @@
     <!-- Allows an application to manage the companion devices.
          @hide -->
     <permission android:name="android.permission.MANAGE_COMPANION_DEVICES"
-                android:protectionLevel="signature" />
+                android:protectionLevel="signature|role" />
 
     <!-- Allows an application to subscribe to notifications about the presence status change
          of their associated companion device
@@ -5041,7 +5041,7 @@
     <!-- @TestApi Allows an application to query audio related state.
          @hide -->
     <permission android:name="android.permission.QUERY_AUDIO_STATE"
-                android:protectionLevel="signature" />
+                android:protectionLevel="signature|role" />
 
     <!-- Allows an application to modify what effects are applied to all audio
          (matching certain criteria) from any application.
@@ -5114,7 +5114,7 @@
         @hide
     -->
    <permission android:name="android.permission.DEVICE_POWER"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- Allows toggling battery saver on the system.
          Superseded by DEVICE_POWER permission. @hide @SystemApi
@@ -5140,7 +5140,7 @@
 
    <!-- @hide Allows low-level access to tun tap driver -->
     <permission android:name="android.permission.NET_TUNNELING"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- Run as a manufacturer test application, running as the root user.
          Only available when the device is running in manufacturer test mode.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 60d875c..42f789b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2918,6 +2918,11 @@
         com.android.settings.intelligence
     </string>
 
+    <!-- System bluetooth stack package name -->
+    <string name="config_systemBluetoothStack" translatable="false">
+        com.android.bluetooth.services
+    </string>
+
     <!-- Flag indicating that the media framework should not allow changes or mute on any
          stream or global volumes. -->
     <bool name="config_useFixedVolume">false</bool>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 870f549..86bad7f 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -184,6 +184,8 @@
     <public name="safety_protection_display_text" />
     <!-- @hide @SystemApi -->
     <public name="config_systemSettingsIntelligence" />
+    <!-- @hide -->
+    <public name="config_systemBluetoothStack" />
   </staging-public-group>
 
   <staging-public-group type="dimen" first-id="0x01db0000">
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 58a2073..04ead1b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -33,6 +33,30 @@
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.bluetooth.services">
+        <permission name="android.permission.DUMP"/>
+        <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.TETHER_PRIVILEGED"/>
+        <permission name="android.permission.CALL_PRIVILEGED"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+        <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+        <permission name="android.permission.UPDATE_DEVICE_STATS"/>
+        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+        <permission name="android.permission.NFC_HANDOVER_STATUS"/>
+        <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+        <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
+        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+        <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+        <permission name="android.permission.REAL_GET_TASKS"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
+        <permission name="android.permission.WRITE_APN_SETTINGS"/>
+        <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+    </privapp-permissions>
+
     <privapp-permissions package="com.android.backupconfirm">
         <permission name="android.permission.BACKUP"/>
         <permission name="android.permission.CRYPT_KEEPER"/>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index e528df8..3f0b01b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -131,7 +131,7 @@
                 mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
                 mRootTaskInfo.configuration, this /* layoutChangeListener */,
                 mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer(),
-                true /* applyDismissingParallax */);
+                SplitLayout.PARALLAX_DISMISSING);
         mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout);
 
         final WindowContainerToken token1 = task1.token;
@@ -327,13 +327,15 @@
     @Override
     public void onLayoutPositionChanging(SplitLayout layout) {
         mSyncQueue.runInSync(t ->
-                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
+                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
+                        true /* applyResizingOffset */));
     }
 
     @Override
     public void onLayoutSizeChanging(SplitLayout layout) {
         mSyncQueue.runInSync(t ->
-                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
+                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
+                        true /* applyResizingOffset */));
     }
 
     @Override
@@ -342,7 +344,8 @@
         layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2);
         mSyncQueue.queue(wct);
         mSyncQueue.runInSync(t ->
-                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
+                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
+                        false /* applyResizingOffset */));
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 577ced5..42ac195 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -48,18 +48,16 @@
  * Controls the window animation run when a user initiates a back gesture.
  */
 public class BackAnimationController implements RemoteCallable<BackAnimationController> {
-
-    private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP =
-            "persist.debug.back_predictability_progress_threshold";
-    // By default, enable new back dispatching without any animations.
-    private static final int BACK_PREDICTABILITY_PROP =
-            SystemProperties.getInt("persist.debug.back_predictability", 1);
-    public static final boolean IS_ENABLED = BACK_PREDICTABILITY_PROP > 0;
-    private static final int PROGRESS_THRESHOLD = SystemProperties
-            .getInt(BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP, -1);
     private static final String TAG = "BackAnimationController";
+    private static final String PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP =
+            "persist.wm.debug.predictive_back_progress_threshold";
+    public static final boolean IS_ENABLED =
+            SystemProperties.getInt("persist.wm.debug.predictive_back", 1) != 0;
+    private static final int PROGRESS_THRESHOLD = SystemProperties
+            .getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1);
     @VisibleForTesting
-    boolean mEnableAnimations = (BACK_PREDICTABILITY_PROP & (1 << 1)) != 0;
+    boolean mEnableAnimations = SystemProperties.getInt(
+            "persist.wm.debug.predictive_back_anim", 0) != 0;
 
     /**
      * Location of the initial touch event of the back gesture.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 116d352..ec81d23 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -72,6 +72,10 @@
  */
 public final class SplitLayout implements DisplayInsetsController.OnInsetsChangedListener {
 
+    public static final int PARALLAX_NONE = 0;
+    public static final int PARALLAX_DISMISSING = 1;
+    public static final int PARALLAX_ALIGN_CENTER = 2;
+
     private final int mDividerWindowWidth;
     private final int mDividerInsets;
     private final int mDividerSize;
@@ -87,7 +91,7 @@
     private final SplitWindowManager mSplitWindowManager;
     private final DisplayImeController mDisplayImeController;
     private final ImePositionProcessor mImePositionProcessor;
-    private final DismissingEffectPolicy mDismissingEffectPolicy;
+    private final ResizingEffectPolicy mSurfaceEffectPolicy;
     private final ShellTaskOrganizer mTaskOrganizer;
     private final InsetsState mInsetsState = new InsetsState();
 
@@ -105,7 +109,7 @@
             SplitLayoutHandler splitLayoutHandler,
             SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
             DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer,
-            boolean applyDismissingParallax) {
+            int parallaxType) {
         mContext = context.createConfigurationContext(configuration);
         mOrientation = configuration.orientation;
         mRotation = configuration.windowConfiguration.getRotation();
@@ -115,7 +119,7 @@
                 parentContainerCallbacks);
         mTaskOrganizer = taskOrganizer;
         mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
-        mDismissingEffectPolicy = new DismissingEffectPolicy(applyDismissingParallax);
+        mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType);
 
         final Resources resources = context.getResources();
         mDividerSize = resources.getDimensionPixelSize(R.dimen.split_divider_bar_width);
@@ -281,7 +285,7 @@
         }
         DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */);
         DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */);
-        mDismissingEffectPolicy.applyDividerPosition(position, isLandscape);
+        mSurfaceEffectPolicy.applyDividerPosition(position, isLandscape);
     }
 
     /** Inflates {@link DividerView} on the root surface. */
@@ -486,7 +490,8 @@
 
     /** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */
     public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1,
-            SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
+            SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2,
+            boolean applyResizingOffset) {
         final SurfaceControl dividerLeash = getDividerLeash();
         if (dividerLeash != null) {
             mTempRect.set(getRefDividerBounds());
@@ -506,7 +511,10 @@
             return;
         }
 
-        mDismissingEffectPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2);
+        mSurfaceEffectPolicy.adjustDimSurface(t, dimLayer1, dimLayer2);
+        if (applyResizingOffset) {
+            mSurfaceEffectPolicy.adjustRootSurface(t, leash1, leash2);
+        }
     }
 
     /** Apply recorded task layout to the {@link WindowContainerTransaction}. */
@@ -590,7 +598,7 @@
          * Calls when resizing the split bounds.
          *
          * @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl,
-         * SurfaceControl, SurfaceControl)
+         * SurfaceControl, SurfaceControl, boolean)
          */
         void onLayoutSizeChanging(SplitLayout layout);
 
@@ -600,7 +608,7 @@
          * @see #applyTaskChanges(WindowContainerTransaction, ActivityManager.RunningTaskInfo,
          * ActivityManager.RunningTaskInfo)
          * @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl,
-         * SurfaceControl, SurfaceControl)
+         * SurfaceControl, SurfaceControl, boolean)
          */
         void onLayoutSizeChanged(SplitLayout layout);
 
@@ -609,7 +617,7 @@
          * panel.
          *
          * @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl,
-         * SurfaceControl, SurfaceControl)
+         * SurfaceControl, SurfaceControl, boolean)
          */
         void onLayoutPositionChanging(SplitLayout layout);
 
@@ -637,21 +645,25 @@
      * Calculates and applies proper dismissing parallax offset and dimming value to hint users
      * dismissing gesture.
      */
-    private class DismissingEffectPolicy {
+    private class ResizingEffectPolicy {
         /** Indicates whether to offset splitting bounds to hint dismissing progress or not. */
-        private final boolean mApplyParallax;
+        private final int mParallaxType;
+
+        int mShrinkSide = DOCKED_INVALID;
 
         // The current dismissing side.
         int mDismissingSide = DOCKED_INVALID;
 
         // The parallax offset to hint the dismissing side and progress.
-        final Point mDismissingParallaxOffset = new Point();
+        final Point mParallaxOffset = new Point();
 
         // The dimming value to hint the dismissing side and progress.
         float mDismissingDimValue = 0.0f;
+        final Rect mContentBounds = new Rect();
+        final Rect mSurfaceBounds = new Rect();
 
-        DismissingEffectPolicy(boolean applyDismissingParallax) {
-            mApplyParallax = applyDismissingParallax;
+        ResizingEffectPolicy(int parallaxType) {
+            mParallaxType = parallaxType;
         }
 
         /**
@@ -662,7 +674,7 @@
          */
         void applyDividerPosition(int position, boolean isLandscape) {
             mDismissingSide = DOCKED_INVALID;
-            mDismissingParallaxOffset.set(0, 0);
+            mParallaxOffset.set(0, 0);
             mDismissingDimValue = 0;
 
             int totalDismissingDistance = 0;
@@ -676,15 +688,39 @@
                         - mDividerSnapAlgorithm.getDismissEndTarget().position;
             }
 
+            final boolean topLeftShrink = isLandscape
+                    ? position < mWinBounds1.right : position < mWinBounds1.bottom;
+            if (topLeftShrink) {
+                mShrinkSide = isLandscape ? DOCKED_LEFT : DOCKED_TOP;
+                mContentBounds.set(mWinBounds1);
+                mSurfaceBounds.set(mBounds1);
+            } else {
+                mShrinkSide = isLandscape ? DOCKED_RIGHT : DOCKED_BOTTOM;
+                mContentBounds.set(mWinBounds2);
+                mSurfaceBounds.set(mBounds2);
+            }
+
             if (mDismissingSide != DOCKED_INVALID) {
                 float fraction = Math.max(0,
                         Math.min(mDividerSnapAlgorithm.calculateDismissingFraction(position), 1f));
                 mDismissingDimValue = DIM_INTERPOLATOR.getInterpolation(fraction);
-                fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
+                if (mParallaxType == PARALLAX_DISMISSING) {
+                    fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
+                    if (isLandscape) {
+                        mParallaxOffset.x = (int) (fraction * totalDismissingDistance);
+                    } else {
+                        mParallaxOffset.y = (int) (fraction * totalDismissingDistance);
+                    }
+                }
+            }
+
+            if (mParallaxType == PARALLAX_ALIGN_CENTER) {
                 if (isLandscape) {
-                    mDismissingParallaxOffset.x = (int) (fraction * totalDismissingDistance);
+                    mParallaxOffset.x =
+                            (mSurfaceBounds.width() - mContentBounds.width()) / 2;
                 } else {
-                    mDismissingParallaxOffset.y = (int) (fraction * totalDismissingDistance);
+                    mParallaxOffset.y =
+                            (mSurfaceBounds.height() - mContentBounds.height()) / 2;
                 }
             }
         }
@@ -704,41 +740,66 @@
         }
 
         /** Applies parallax offset and dimming value to the root surface at the dismissing side. */
-        boolean adjustDismissingSurface(SurfaceControl.Transaction t,
-                SurfaceControl leash1, SurfaceControl leash2,
+        void adjustRootSurface(SurfaceControl.Transaction t,
+                SurfaceControl leash1, SurfaceControl leash2) {
+            SurfaceControl targetLeash = null;
+
+            if (mParallaxType == PARALLAX_DISMISSING) {
+                switch (mDismissingSide) {
+                    case DOCKED_TOP:
+                    case DOCKED_LEFT:
+                        targetLeash = leash1;
+                        mTempRect.set(mBounds1);
+                        break;
+                    case DOCKED_BOTTOM:
+                    case DOCKED_RIGHT:
+                        targetLeash = leash2;
+                        mTempRect.set(mBounds2);
+                        break;
+                }
+            } else if (mParallaxType == PARALLAX_ALIGN_CENTER) {
+                switch (mShrinkSide) {
+                    case DOCKED_TOP:
+                    case DOCKED_LEFT:
+                        targetLeash = leash1;
+                        mTempRect.set(mBounds1);
+                        break;
+                    case DOCKED_BOTTOM:
+                    case DOCKED_RIGHT:
+                        targetLeash = leash2;
+                        mTempRect.set(mBounds2);
+                        break;
+                }
+            }
+            if (mParallaxType != PARALLAX_NONE && targetLeash != null) {
+                t.setPosition(targetLeash,
+                        mTempRect.left + mParallaxOffset.x, mTempRect.top + mParallaxOffset.y);
+                // Transform the screen-based split bounds to surface-based crop bounds.
+                mTempRect.offsetTo(-mParallaxOffset.x, -mParallaxOffset.y);
+                t.setWindowCrop(targetLeash, mTempRect);
+            }
+        }
+
+        void adjustDimSurface(SurfaceControl.Transaction t,
                 SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
-            SurfaceControl targetLeash, targetDimLayer;
+            SurfaceControl targetDimLayer;
             switch (mDismissingSide) {
                 case DOCKED_TOP:
                 case DOCKED_LEFT:
-                    targetLeash = leash1;
                     targetDimLayer = dimLayer1;
-                    mTempRect.set(mBounds1);
                     break;
                 case DOCKED_BOTTOM:
                 case DOCKED_RIGHT:
-                    targetLeash = leash2;
                     targetDimLayer = dimLayer2;
-                    mTempRect.set(mBounds2);
                     break;
                 case DOCKED_INVALID:
                 default:
                     t.setAlpha(dimLayer1, 0).hide(dimLayer1);
                     t.setAlpha(dimLayer2, 0).hide(dimLayer2);
-                    return false;
-            }
-
-            if (mApplyParallax) {
-                t.setPosition(targetLeash,
-                        mTempRect.left + mDismissingParallaxOffset.x,
-                        mTempRect.top + mDismissingParallaxOffset.y);
-                // Transform the screen-based split bounds to surface-based crop bounds.
-                mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y);
-                t.setWindowCrop(targetLeash, mTempRect);
+                    return;
             }
             t.setAlpha(targetDimLayer, mDismissingDimValue)
                     .setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
-            return true;
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 73f3931..7f9ecca2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -63,6 +63,7 @@
 import com.android.wm.shell.pip.phone.PhonePipMenuController;
 import com.android.wm.shell.pip.phone.PipAppOpsListener;
 import com.android.wm.shell.pip.phone.PipController;
+import com.android.wm.shell.pip.phone.PipKeepClearAlgorithm;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
 import com.android.wm.shell.pip.phone.PipTouchHandler;
 import com.android.wm.shell.recents.RecentTasksController;
@@ -207,7 +208,8 @@
     @Provides
     static Optional<Pip> providePip(Context context, DisplayController displayController,
             PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
-            PipBoundsState pipBoundsState, PipMediaController pipMediaController,
+            PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState,
+            PipMotionHelper pipMotionHelper, PipMediaController pipMediaController,
             PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
             PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
             WindowManagerShellWrapper windowManagerShellWrapper,
@@ -215,9 +217,11 @@
             Optional<OneHandedController> oneHandedController,
             @ShellMainThread ShellExecutor mainExecutor) {
         return Optional.ofNullable(PipController.create(context, displayController,
-                pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, pipMediaController,
-                phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController,
-                windowManagerShellWrapper, taskStackListener, oneHandedController, mainExecutor));
+                pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState,
+                pipMotionHelper,
+                pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTouchHandler,
+                pipTransitionController, windowManagerShellWrapper, taskStackListener,
+                oneHandedController, mainExecutor));
     }
 
     @WMSingleton
@@ -234,6 +238,12 @@
 
     @WMSingleton
     @Provides
+    static PipKeepClearAlgorithm providePipKeepClearAlgorithm() {
+        return new PipKeepClearAlgorithm();
+    }
+
+    @WMSingleton
+    @Provides
     static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
             PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) {
         return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm);
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 623ef05..175a244 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
@@ -107,7 +107,9 @@
     private PipAppOpsListener mAppOpsListener;
     private PipMediaController mMediaController;
     private PipBoundsAlgorithm mPipBoundsAlgorithm;
+    private PipKeepClearAlgorithm mPipKeepClearAlgorithm;
     private PipBoundsState mPipBoundsState;
+    private PipMotionHelper mPipMotionHelper;
     private PipTouchHandler mTouchHandler;
     private PipTransitionController mPipTransitionController;
     private TaskStackListenerImpl mTaskStackListener;
@@ -241,6 +243,10 @@
                         Set<Rect> unrestricted) {
                     if (mPipBoundsState.getDisplayId() == displayId) {
                         mPipBoundsState.setKeepClearAreas(restricted, unrestricted);
+                        mPipMotionHelper.moveToBounds(mPipKeepClearAlgorithm.adjust(
+                                mPipBoundsState.getBounds(),
+                                mPipBoundsState.getRestrictedKeepClearAreas(),
+                                mPipBoundsState.getUnrestrictedKeepClearAreas()));
                     }
                 }
             };
@@ -293,7 +299,8 @@
     @Nullable
     public static Pip create(Context context, DisplayController displayController,
             PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
-            PipBoundsState pipBoundsState, PipMediaController pipMediaController,
+            PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState,
+            PipMotionHelper pipMotionHelper, PipMediaController pipMediaController,
             PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
             PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
             WindowManagerShellWrapper windowManagerShellWrapper,
@@ -307,9 +314,9 @@
         }
 
         return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm,
-                pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer,
-                pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
-                taskStackListener, oneHandedController, mainExecutor)
+                pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController,
+                phonePipMenuController, pipTaskOrganizer,  pipTouchHandler, pipTransitionController,
+                windowManagerShellWrapper, taskStackListener, oneHandedController, mainExecutor)
                 .mImpl;
     }
 
@@ -317,7 +324,9 @@
             DisplayController displayController,
             PipAppOpsListener pipAppOpsListener,
             PipBoundsAlgorithm pipBoundsAlgorithm,
+            PipKeepClearAlgorithm pipKeepClearAlgorithm,
             @NonNull PipBoundsState pipBoundsState,
+            PipMotionHelper pipMotionHelper,
             PipMediaController pipMediaController,
             PhonePipMenuController phonePipMenuController,
             PipTaskOrganizer pipTaskOrganizer,
@@ -339,7 +348,9 @@
         mWindowManagerShellWrapper = windowManagerShellWrapper;
         mDisplayController = displayController;
         mPipBoundsAlgorithm = pipBoundsAlgorithm;
+        mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
         mPipBoundsState = pipBoundsState;
+        mPipMotionHelper = pipMotionHelper;
         mPipTaskOrganizer = pipTaskOrganizer;
         mMainExecutor = mainExecutor;
         mMediaController = pipMediaController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java
new file mode 100644
index 0000000..a83258f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip.phone;
+
+import android.graphics.Rect;
+
+import java.util.Set;
+
+/**
+ * Calculates the adjusted position that does not occlude keep clear areas.
+ */
+public class PipKeepClearAlgorithm {
+
+    /** Returns a new {@code Rect} that does not occlude the provided keep clear areas. */
+    public Rect adjust(Rect defaultBounds, Set<Rect> restrictedKeepClearAreas,
+            Set<Rect> unrestrictedKeepClearAreas) {
+        if (restrictedKeepClearAreas.isEmpty()) {
+            return defaultBounds;
+        }
+        // TODO(b/183746978): implement the adjustment algorithm
+        // naively check if areas intersect, an if so move PiP upwards
+        Rect outBounds = new Rect(defaultBounds);
+        for (Rect r : restrictedKeepClearAreas) {
+            if (r.intersect(outBounds)) {
+                outBounds.offset(0, r.top - outBounds.bottom);
+            }
+        }
+        return outBounds;
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index f20870f..aec51ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -377,7 +377,8 @@
                     return;
                 }
 
-                mStageCoordinator.updateSurfaceBounds(null /* layout */, t);
+                mStageCoordinator.updateSurfaceBounds(null /* layout */, t,
+                        false /* applyResizingOffset */);
                 for (int i = 0; i < apps.length; ++i) {
                     if (apps[i].mode == MODE_OPENING) {
                         t.show(apps[i].leash);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index e150cf9..45931de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -30,6 +30,7 @@
 import static android.view.WindowManager.transitTypeToString;
 import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 
+import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -495,7 +496,8 @@
 
         // Using legacy transitions, so we can't use blast sync since it conflicts.
         mTaskOrganizer.applyTransaction(wct);
-        mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t));
+        mSyncQueue.runInSync(t ->
+                updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */));
     }
 
     /**
@@ -704,9 +706,11 @@
         mMainStage.deactivate(wct, !fromEnteringPip && mMainStage == childrenToTop);
         wct.reorder(mRootTaskInfo.token, false /* onTop */);
         mTaskOrganizer.applyTransaction(wct);
-        mSyncQueue.runInSync(t -> t
-                .setWindowCrop(mMainStage.mRootLeash, null)
-                .setWindowCrop(mSideStage.mRootLeash, null));
+        mSyncQueue.runInSync(t -> {
+            setResizingSplits(false /* resizing */);
+            t.setWindowCrop(mMainStage.mRootLeash, null)
+                    .setWindowCrop(mSideStage.mRootLeash, null);
+        });
 
         // Hide divider and reset its position.
         mSplitLayout.resetDividerPosition();
@@ -780,7 +784,7 @@
     void finishEnterSplitScreen(SurfaceControl.Transaction t) {
         mSplitLayout.init();
         setDividerVisibility(true, t);
-        updateSurfaceBounds(mSplitLayout, t);
+        updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
         setSplitsVisible(true);
         mShouldUpdateRecents = true;
         updateRecentTasksSplitPair();
@@ -925,7 +929,8 @@
         if (mSplitLayout == null) {
             mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
                     mRootTaskInfo.configuration, this, mParentContainerCallbacks,
-                    mDisplayImeController, mTaskOrganizer, false /* applyDismissingParallax */);
+                    mDisplayImeController, mTaskOrganizer,
+                    PARALLAX_ALIGN_CENTER /* parallaxType */);
             mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
         }
 
@@ -1075,7 +1080,7 @@
             prepareEnterSplitScreen(wct);
             mSyncQueue.queue(wct);
             mSyncQueue.runInSync(t -> {
-                updateSurfaceBounds(mSplitLayout, t);
+                updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
                 setDividerVisibility(true, t);
             });
         }
@@ -1094,8 +1099,6 @@
 
     @Override
     public void onSnappedToDismiss(boolean bottomOrRight) {
-        setResizingSplits(false /* resizing */);
-
         final boolean mainStageToTop =
                 bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
                         : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
@@ -1104,6 +1107,7 @@
             return;
         }
 
+        setResizingSplits(false /* resizing */);
         final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         prepareExitSplitScreen(dismissTop, wct);
@@ -1121,14 +1125,14 @@
 
     @Override
     public void onLayoutPositionChanging(SplitLayout layout) {
-        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, false /* applyResizingOffset */));
     }
 
     @Override
     public void onLayoutSizeChanging(SplitLayout layout) {
         mSyncQueue.runInSync(t -> {
             setResizingSplits(true /* resizing */);
-            updateSurfaceBounds(layout, t);
+            updateSurfaceBounds(layout, t, true /* applyResizingOffset */);
             mMainStage.onResizing(getMainStageBounds(), t);
             mSideStage.onResizing(getSideStageBounds(), t);
         });
@@ -1142,7 +1146,7 @@
         mSyncQueue.queue(wct);
         mSyncQueue.runInSync(t -> {
             setResizingSplits(false /* resizing */);
-            updateSurfaceBounds(layout, t);
+            updateSurfaceBounds(layout, t, false /* applyResizingOffset */);
             mMainStage.onResized(t);
             mSideStage.onResized(t);
         });
@@ -1174,13 +1178,15 @@
         layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
     }
 
-    void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) {
+    void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t,
+            boolean applyResizingOffset) {
         final StageTaskListener topLeftStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
         final StageTaskListener bottomRightStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
         (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
-                bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer);
+                bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer,
+                applyResizingOffset);
     }
 
     void setResizingSplits(boolean resizing) {
@@ -1220,7 +1226,6 @@
         mTaskOrganizer.applyTransaction(wct);
     }
 
-    @Override
     public void onDisplayAdded(int displayId) {
         if (displayId != DEFAULT_DISPLAY) {
             return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
index f1520ed..0717405 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
@@ -253,7 +253,8 @@
                     RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
                     IRemoteAnimationFinishedCallback finishedCallback,
                     SurfaceControl.Transaction t) {
-                mStageCoordinator.updateSurfaceBounds(null /* layout */, t);
+                mStageCoordinator.updateSurfaceBounds(null /* layout */, t,
+                        false /* applyResizingOffset */);
 
                 if (apps != null) {
                     for (int i = 0; i < apps.length; ++i) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
index 6ef20e3..ac25c75 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
@@ -265,7 +265,8 @@
         mMainStage.activate(getMainStageBounds(), wct);
         mSideStage.addTask(task, getSideStageBounds(), wct);
         mSyncQueue.queue(wct);
-        mSyncQueue.runInSync(t -> updateSurfaceBounds(null /* layout */, t));
+        mSyncQueue.runInSync(
+                t -> updateSurfaceBounds(null /* layout */, t, false /* applyResizingOffset */));
         return true;
     }
 
@@ -801,12 +802,12 @@
 
     @Override
     public void onLayoutPositionChanging(SplitLayout layout) {
-        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */));
     }
 
     @Override
     public void onLayoutSizeChanging(SplitLayout layout) {
-        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */));
         mSideStage.setOutlineVisibility(false);
     }
 
@@ -816,7 +817,7 @@
         updateWindowBounds(layout, wct);
         updateUnfoldBounds();
         mSyncQueue.queue(wct);
-        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+        mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, false /* applyResizingOffset */));
         mSideStage.setOutlineVisibility(true);
         mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
     }
@@ -840,13 +841,15 @@
         layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
     }
 
-    void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) {
+    void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t,
+            boolean applyResizingOffset) {
         final StageTaskListener topLeftStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
         final StageTaskListener bottomRightStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
         (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
-                bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer);
+                bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer,
+                applyResizingOffset);
     }
 
     @Override
@@ -882,7 +885,7 @@
         if (mSplitLayout == null) {
             mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
                     mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
-                    mDisplayImeController, mTaskOrganizer, true /* applyDismissingParallax */);
+                    mDisplayImeController, mTaskOrganizer, SplitLayout.PARALLAX_DISMISSING);
             mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
 
             if (mMainUnfoldController != null && mSideUnfoldController != null) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 8b4e1f8..f1e602f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -73,7 +73,7 @@
                 mCallbacks,
                 mDisplayImeController,
                 mTaskOrganizer,
-                false /* applyDismissingParallax */));
+                SplitLayout.PARALLAX_NONE));
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index aef298e..deb955b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -74,6 +74,7 @@
     @Mock private PhonePipMenuController mMockPhonePipMenuController;
     @Mock private PipAppOpsListener mMockPipAppOpsListener;
     @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
+    @Mock private PipKeepClearAlgorithm mMockPipKeepClearAlgorithm;
     @Mock private PipSnapAlgorithm mMockPipSnapAlgorithm;
     @Mock private PipMediaController mMockPipMediaController;
     @Mock private PipTaskOrganizer mMockPipTaskOrganizer;
@@ -97,9 +98,10 @@
             return null;
         }).when(mMockExecutor).execute(any());
         mPipController = new PipController(mContext, mMockDisplayController,
-                mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState,
-                mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
-                mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
+                mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
+                mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
+                mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler,
+                mMockPipTransitionController, mMockWindowManagerShellWrapper,
                 mMockTaskStackListener, mMockOneHandedController, mMockExecutor);
         when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
         when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
@@ -128,9 +130,10 @@
         when(spyContext.getPackageManager()).thenReturn(mockPackageManager);
 
         assertNull(PipController.create(spyContext, mMockDisplayController,
-                mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState,
-                mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
-                mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
+                mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
+                mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
+                mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler,
+                mMockPipTransitionController, mMockWindowManagerShellWrapper,
                 mMockTaskStackListener, mMockOneHandedController, mMockExecutor));
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java
new file mode 100644
index 0000000..f657b5e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+/**
+ * Unit tests against {@link PipKeepClearAlgorithm}.
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipKeepClearAlgorithmTest extends ShellTestCase {
+
+    private PipKeepClearAlgorithm mPipKeepClearAlgorithm;
+
+
+    @Before
+    public void setUp() throws Exception {
+        mPipKeepClearAlgorithm = new PipKeepClearAlgorithm();
+    }
+
+    @Test
+    public void adjust_withCollidingRestrictedKeepClearAreas_movesBounds() {
+        final Rect inBounds = new Rect(0, 0, 100, 100);
+        final Rect keepClearRect = new Rect(50, 50, 150, 150);
+
+        final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect),
+                Set.of());
+
+        assertFalse(outBounds.contains(keepClearRect));
+    }
+
+    @Test
+    public void adjust_withNonCollidingRestrictedKeepClearAreas_boundsDoNotChange() {
+        final Rect inBounds = new Rect(0, 0, 100, 100);
+        final Rect keepClearRect = new Rect(100, 100, 150, 150);
+
+        final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect),
+                Set.of());
+
+        assertEquals(inBounds, outBounds);
+    }
+
+    @Test
+    public void adjust_withCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() {
+        // TODO(b/183746978): update this test to accommodate for the updated algorithm
+        final Rect inBounds = new Rect(0, 0, 100, 100);
+        final Rect keepClearRect = new Rect(50, 50, 150, 150);
+
+        final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(),
+                Set.of(keepClearRect));
+
+        assertEquals(inBounds, outBounds);
+    }
+
+    @Test
+    public void adjust_withNonCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() {
+        final Rect inBounds = new Rect(0, 0, 100, 100);
+        final Rect keepClearRect = new Rect(100, 100, 150, 150);
+
+        final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(),
+                Set.of(keepClearRect));
+
+        assertEquals(inBounds, outBounds);
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 061136c6..c571d44 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -309,7 +309,7 @@
     public void testFinishEnterSplitScreen_applySurfaceLayout() {
         mStageCoordinator.finishEnterSplitScreen(new SurfaceControl.Transaction());
 
-        verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any());
+        verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any(), eq(false));
     }
 
     private class UnfoldControllerProvider implements
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 120b09a..de35514 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -41,6 +41,7 @@
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.IRecentsAnimationController;
 import android.view.SurfaceControl;
 import android.window.IRemoteTransition;
@@ -244,22 +245,28 @@
         @SuppressLint("NewApi")
         boolean merge(TransitionInfo info, SurfaceControl.Transaction t,
                 RecentsAnimationListener recents) {
-            ArrayList<TransitionInfo.Change> openingTasks = null;
+            SparseArray<TransitionInfo.Change> openingTasks = null;
             boolean cancelRecents = false;
             boolean homeGoingAway = false;
             boolean hasChangingApp = false;
             for (int i = info.getChanges().size() - 1; i >= 0; --i) {
                 final TransitionInfo.Change change = info.getChanges().get(i);
                 if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
-                    if (change.getTaskInfo() != null) {
-                        if (change.getTaskInfo().topActivityType == ACTIVITY_TYPE_HOME) {
+                    final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+                    if (taskInfo != null) {
+                        if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
                             // canceling recents animation
                             cancelRecents = true;
                         }
                         if (openingTasks == null) {
-                            openingTasks = new ArrayList<>();
+                            openingTasks = new SparseArray<>();
                         }
-                        openingTasks.add(change);
+                        if (taskInfo.hasParentTask()) {
+                            // Collects opening leaf tasks only since Launcher monitors leaf task
+                            // ids to perform recents animation.
+                            openingTasks.remove(taskInfo.parentTaskId);
+                        }
+                        openingTasks.put(taskInfo.taskId, change);
                     }
                 } else if (change.getMode() == TRANSIT_CLOSE
                         || change.getMode() == TRANSIT_TO_BACK) {
@@ -287,7 +294,7 @@
             int pauseMatches = 0;
             if (!cancelRecents) {
                 for (int i = 0; i < openingTasks.size(); ++i) {
-                    if (mPausingTasks.contains(openingTasks.get(i).getContainer())) {
+                    if (mPausingTasks.contains(openingTasks.valueAt(i).getContainer())) {
                         ++pauseMatches;
                     }
                 }
@@ -308,10 +315,11 @@
             final RemoteAnimationTargetCompat[] targets =
                     new RemoteAnimationTargetCompat[openingTasks.size()];
             for (int i = 0; i < openingTasks.size(); ++i) {
-                mOpeningLeashes.add(openingTasks.get(i).getLeash());
+                final TransitionInfo.Change change = openingTasks.valueAt(i);
+                mOpeningLeashes.add(change.getLeash());
                 // We are receiving new opening tasks, so convert to onTasksAppeared.
                 final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat(
-                        openingTasks.get(i), layer, info, t);
+                        change, layer, info, t);
                 mLeashMap.put(mOpeningLeashes.get(i), target.leash);
                 t.reparent(target.leash, mInfo.getRootLeash());
                 t.setLayer(target.leash, layer);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 63b2b20..2ac2408 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -376,7 +376,6 @@
                 boolean withinSensorArea =
                         isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView);
                 if (withinSensorArea) {
-                    mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
                     Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0);
                     Log.v(TAG, "onTouch | action down");
                     // The pointer that causes ACTION_DOWN is always at index 0.
@@ -792,6 +791,7 @@
                     + " current: " + mOverlay.getRequestId());
             return;
         }
+        mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
 
         if (!mOnFingerDown) {
             playStartHaptic();
@@ -806,11 +806,9 @@
 
         final UdfpsView view = mOverlay.getOverlayView();
         if (view != null) {
-            Trace.beginAsyncSection("UdfpsController.e2e.startIllumination", 0);
             view.startIllumination(() -> {
                 mFingerprintManager.onUiReady(requestId, mSensorProps.sensorId);
                 mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
-                Trace.endAsyncSection("UdfpsController.e2e.startIllumination", 0);
             });
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 6d727b4..b172e92 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -65,6 +65,7 @@
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
 import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.view.CrossWindowBlurListeners;
@@ -452,6 +453,12 @@
 
     @Provides
     @Singleton
+    static CarrierConfigManager provideCarrierConfigManager(Context context) {
+        return context.getSystemService(CarrierConfigManager.class);
+    }
+
+    @Provides
+    @Singleton
     static WindowManager provideWindowManager(Context context) {
         return context.getSystemService(WindowManager.class);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index c9a61a8..44580aa 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -162,6 +162,17 @@
     public static final SysPropBooleanFlag WM_ENABLE_SHELL_TRANSITIONS =
             new SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false);
 
+    // 1200 - predictive back
+    @Keep
+    public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag(
+            1200, "persist.wm.debug.predictive_back", true);
+    @Keep
+    public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK_ANIM = new SysPropBooleanFlag(
+            1201, "persist.wm.debug.predictive_back_anim", false);
+    @Keep
+    public static final SysPropBooleanFlag WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
+            new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false);
+
     // Pay no attention to the reflection behind the curtain.
     // ========================== Curtain ==========================
     // |                                                           |
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 96ae646..290bf0d 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -41,26 +41,23 @@
 
 import javax.inject.Inject;
 
-import dagger.Lazy;
-
 public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks {
 
     private final Context mContext;
-    private final Lazy<GlobalActionsDialogLite> mGlobalActionsDialogLazy;
     private final KeyguardStateController mKeyguardStateController;
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final BlurUtils mBlurUtils;
     private final CommandQueue mCommandQueue;
-    private GlobalActionsDialogLite mGlobalActionsDialog;
+    private final GlobalActionsDialogLite mGlobalActionsDialog;
     private boolean mDisabled;
 
     @Inject
     public GlobalActionsImpl(Context context, CommandQueue commandQueue,
-            Lazy<GlobalActionsDialogLite> globalActionsDialogLazy, BlurUtils blurUtils,
+            GlobalActionsDialogLite globalActionsDialog, BlurUtils blurUtils,
             KeyguardStateController keyguardStateController,
             DeviceProvisionedController deviceProvisionedController) {
         mContext = context;
-        mGlobalActionsDialogLazy = globalActionsDialogLazy;
+        mGlobalActionsDialog = globalActionsDialog;
         mKeyguardStateController = keyguardStateController;
         mDeviceProvisionedController = deviceProvisionedController;
         mCommandQueue = commandQueue;
@@ -71,16 +68,12 @@
     @Override
     public void destroy() {
         mCommandQueue.removeCallback(this);
-        if (mGlobalActionsDialog != null) {
-            mGlobalActionsDialog.destroy();
-            mGlobalActionsDialog = null;
-        }
+        mGlobalActionsDialog.destroy();
     }
 
     @Override
     public void showGlobalActions(GlobalActionsManager manager) {
         if (mDisabled) return;
-        mGlobalActionsDialog = mGlobalActionsDialogLazy.get();
         mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(),
                 mDeviceProvisionedController.isDeviceProvisioned(), null /* view */);
     }
@@ -189,7 +182,7 @@
         final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0;
         if (displayId != mContext.getDisplayId() || disabled == mDisabled) return;
         mDisabled = disabled;
-        if (disabled && mGlobalActionsDialog != null) {
+        if (disabled) {
             mGlobalActionsDialog.dismissDialog();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 05b2c50..3a727ba 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -681,7 +681,6 @@
                     button.setOnClickListener(v -> {
                         if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
                             mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId);
-                            mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId);
                             logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT);
                             action.run();
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index 3c6805b..cd86fff 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -19,6 +19,7 @@
 import android.app.StatusBarManager
 import android.content.Context
 import android.media.MediaRoute2Info
+import android.util.Log
 import android.view.View
 import androidx.annotation.StringRes
 import com.android.internal.logging.UiEventLogger
@@ -221,7 +222,12 @@
          */
         fun getSenderStateFromId(
             @StatusBarManager.MediaTransferSenderState displayState: Int,
-        ): ChipStateSender = values().first { it.stateInt == displayState }
+        ): ChipStateSender? = try {
+            values().first { it.stateInt == displayState }
+        } catch (e: NoSuchElementException) {
+            Log.e(TAG, "Could not find requested state $displayState", e)
+            null
+        }
 
         /**
          * Returns the state int from [StatusBarManager] associated with the given sender state
@@ -238,3 +244,5 @@
 // process and we should keep the user informed about it as long as possible (but don't allow it to
 // continue indefinitely).
 private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 15000L
+
+private const val TAG = "ChipStateSender"
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index f5abe28..40265a3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -143,6 +143,7 @@
     private boolean mDeadZoneConsuming = false;
     private final NavigationBarTransitions mBarTransitions;
     private final OverviewProxyService mOverviewProxyService;
+    @Nullable
     private AutoHideController mAutoHideController;
 
     // performs manual animation in sync with layout transitions
@@ -316,7 +317,7 @@
             };
 
     private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> {
-        if (visible) {
+        if (visible && mAutoHideController != null) {
             mAutoHideController.touchAutoHide();
         }
         notifyActiveTouchRegions();
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 2435497..3e00a5f 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -18,6 +18,7 @@
 
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
 
+import android.app.Dialog;
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -60,20 +61,25 @@
 import com.android.settingslib.utils.PowerUtil;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIApplication;
+import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.NotificationChannels;
 import com.android.systemui.volume.Events;
 
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.text.NumberFormat;
 import java.util.Locale;
 import java.util.Objects;
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
+
 /**
  */
 @SysUISingleton
@@ -164,11 +170,15 @@
     private ActivityStarter mActivityStarter;
     private final BroadcastSender mBroadcastSender;
 
+    private final Lazy<BatteryController> mBatteryControllerLazy;
+    private final DialogLaunchAnimator mDialogLaunchAnimator;
+
     /**
      */
     @Inject
     public PowerNotificationWarnings(Context context, ActivityStarter activityStarter,
-            BroadcastSender broadcastSender) {
+            BroadcastSender broadcastSender, Lazy<BatteryController> batteryControllerLazy,
+            DialogLaunchAnimator dialogLaunchAnimator) {
         mContext = context;
         mNoMan = mContext.getSystemService(NotificationManager.class);
         mPowerMan = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -176,6 +186,8 @@
         mReceiver.init();
         mActivityStarter = activityStarter;
         mBroadcastSender = broadcastSender;
+        mBatteryControllerLazy = batteryControllerLazy;
+        mDialogLaunchAnimator = dialogLaunchAnimator;
         mUseSevereDialog = mContext.getResources().getBoolean(R.bool.config_severe_battery_dialog);
     }
 
@@ -685,8 +697,19 @@
         }
         d.setShowForAllUsers(true);
         d.setOnDismissListener((dialog) -> mSaverConfirmation = null);
-        d.show();
+        WeakReference<View> ref = mBatteryControllerLazy.get().getLastPowerSaverStartView();
+        if (ref != null && ref.get() != null && ref.get().isAggregatedVisible()) {
+            mDialogLaunchAnimator.showFromView(d, ref.get());
+        } else {
+            d.show();
+        }
         mSaverConfirmation = d;
+        mBatteryControllerLazy.get().clearLastPowerSaverStartView();
+    }
+
+    @VisibleForTesting
+    Dialog getSaverConfirmationDialog() {
+        return mSaverConfirmation;
     }
 
     private boolean isEnglishLocale() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 7d8a28f..1004fca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -116,6 +116,11 @@
     public void handleSetListening(boolean listening) {
         super.handleSetListening(listening);
         mSetting.setListening(listening);
+        if (!listening) {
+            // If we stopped listening, it means that the tile is not visible. In that case, we
+            // don't need to save the view anymore
+            mBatteryController.clearLastPowerSaverStartView();
+        }
     }
 
     @Override
@@ -128,7 +133,7 @@
         if (getState().state == Tile.STATE_UNAVAILABLE) {
             return;
         }
-        mBatteryController.setPowerSaveMode(!mPowerSave);
+        mBatteryController.setPowerSaveMode(!mPowerSave, view);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index d785059..27586b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -60,6 +60,7 @@
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.SecureSettings;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -84,6 +85,7 @@
 
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final KeyguardStateController mKeyguardStateController;
+    private final SecureSettings mSecureSettings;
     private final Object mLock = new Object();
 
     // Lazy
@@ -187,6 +189,7 @@
     protected NotificationPresenter mPresenter;
     protected ContentObserver mLockscreenSettingsObserver;
     protected ContentObserver mSettingsObserver;
+    private boolean mHideSilentNotificationsOnLockscreen;
 
     private NotificationEntryManager getEntryManager() {
         if (mEntryManager == null) {
@@ -208,6 +211,7 @@
             @Main Handler mainHandler,
             DeviceProvisionedController deviceProvisionedController,
             KeyguardStateController keyguardStateController,
+            SecureSettings secureSettings,
             DumpManager dumpManager) {
         mContext = context;
         mMainHandler = mainHandler;
@@ -222,6 +226,7 @@
         mKeyguardManager = keyguardManager;
         mBroadcastDispatcher = broadcastDispatcher;
         mDeviceProvisionedController = deviceProvisionedController;
+        mSecureSettings = secureSettings;
         mKeyguardStateController = keyguardStateController;
 
         dumpManager.registerDumpable(this);
@@ -256,12 +261,18 @@
         };
 
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
+                mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
                 mLockscreenSettingsObserver,
                 UserHandle.USER_ALL);
 
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+                mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+                true,
+                mLockscreenSettingsObserver,
+                UserHandle.USER_ALL);
+
+        mContext.getContentResolver().registerContentObserver(
+                mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS),
                 true,
                 mLockscreenSettingsObserver,
                 UserHandle.USER_ALL);
@@ -272,7 +283,7 @@
 
         if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
             mContext.getContentResolver().registerContentObserver(
-                    Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
+                    mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
                     false,
                     mSettingsObserver,
                     UserHandle.USER_ALL);
@@ -366,7 +377,7 @@
             }
         }
         boolean exceedsPriorityThreshold;
-        if (hideSilentNotificationsOnLockscreen()) {
+        if (mHideSilentNotificationsOnLockscreen) {
             exceedsPriorityThreshold =
                     entry.getBucket() == BUCKET_MEDIA_CONTROLS
                             || (entry.getBucket() != BUCKET_SILENT
@@ -377,11 +388,6 @@
         return mShowLockscreenNotifications && exceedsPriorityThreshold;
     }
 
-    private boolean hideSilentNotificationsOnLockscreen() {
-        return whitelistIpcs(() -> Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1) == 0);
-    }
-
     private void setShowLockscreenNotifications(boolean show) {
         mShowLockscreenNotifications = show;
     }
@@ -391,7 +397,7 @@
     }
 
     protected void updateLockscreenNotificationSetting() {
-        final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+        final boolean show = mSecureSettings.getIntForUser(
                 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
                 1,
                 mCurrentUserId) != 0;
@@ -400,10 +406,13 @@
         final boolean allowedByDpm = (dpmFlags
                 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
 
+        mHideSilentNotificationsOnLockscreen = mSecureSettings.getIntForUser(
+                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1, mCurrentUserId) == 0;
+
         setShowLockscreenNotifications(show && allowedByDpm);
 
         if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
-            final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+            final boolean remoteInput = mSecureSettings.getIntForUser(
                     Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
                     0,
                     mCurrentUserId) != 0;
@@ -426,8 +435,7 @@
         }
 
         if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
-            final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
-                    mContext.getContentResolver(),
+            final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
             final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
                     DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
@@ -492,8 +500,7 @@
         }
 
         if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
-            final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
-                    mContext.getContentResolver(),
+            final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
                     Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
             final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
                     DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
index 4a6d7e1..8d7fc98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
@@ -21,8 +21,6 @@
 
 import com.android.settingslib.WirelessUtils;
 
-import java.util.List;
-
 /** Shows the operator name */
 public class OperatorNameView extends TextView {
     private boolean mDemoMode;
@@ -43,8 +41,10 @@
         mDemoMode = demoMode;
     }
 
-    void update(boolean showOperatorName, boolean hasMobile,
-            List<OperatorNameViewController.SubInfo> subs) {
+    void update(boolean showOperatorName,
+            boolean hasMobile,
+            OperatorNameViewController.SubInfo sub
+    ) {
         setVisibility(showOperatorName ? VISIBLE : GONE);
 
         boolean airplaneMode = WirelessUtils.isAirplaneModeOn(mContext);
@@ -55,24 +55,21 @@
         }
 
         if (!mDemoMode) {
-            updateText(subs);
+            updateText(sub);
         }
     }
 
-    void updateText(List<OperatorNameViewController.SubInfo> subs) {
+    void updateText(OperatorNameViewController.SubInfo subInfo) {
+        CharSequence carrierName = null;
         CharSequence displayText = null;
-        final int N = subs.size();
-        for (int i = 0; i < N; i++) {
-            OperatorNameViewController.SubInfo subInfo = subs.get(i);
-            CharSequence carrierName = subs.get(i).getCarrierName();
-            if (!TextUtils.isEmpty(carrierName) && subInfo.simReady()) {
-                if (subInfo.stateInService()) {
-                    displayText = subInfo.getCarrierName();
-                    break;
-                }
+        if (subInfo != null) {
+            carrierName = subInfo.getCarrierName();
+        }
+        if (!TextUtils.isEmpty(carrierName) && subInfo.simReady()) {
+            if (subInfo.stateInService()) {
+                displayText = carrierName;
             }
         }
-
         setText(displayText);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
index 8a4c4b5..8afc72f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
@@ -20,6 +20,7 @@
 import android.os.Bundle;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.view.View;
 
@@ -30,12 +31,11 @@
 import com.android.systemui.statusbar.connectivity.IconState;
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.connectivity.SignalCallback;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.CarrierConfigTracker;
 import com.android.systemui.util.ViewController;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import javax.inject.Inject;
 
 /** Controller for {@link OperatorNameView}. */
@@ -47,19 +47,22 @@
     private final TunerService mTunerService;
     private final TelephonyManager mTelephonyManager;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final CarrierConfigTracker mCarrierConfigTracker;
 
     private OperatorNameViewController(OperatorNameView view,
             DarkIconDispatcher darkIconDispatcher,
             NetworkController networkController,
             TunerService tunerService,
             TelephonyManager telephonyManager,
-            KeyguardUpdateMonitor keyguardUpdateMonitor) {
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            CarrierConfigTracker carrierConfigTracker) {
         super(view);
         mDarkIconDispatcher = darkIconDispatcher;
         mNetworkController = networkController;
         mTunerService = tunerService;
         mTelephonyManager = telephonyManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mCarrierConfigTracker = carrierConfigTracker;
     }
 
     @Override
@@ -79,24 +82,22 @@
     }
 
     private void update() {
-        mView.update(mTunerService.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0,
-                mTelephonyManager.isDataCapable(), getSubInfos());
+        SubInfo defaultSubInfo = getDefaultSubInfo();
+        boolean showOperatorName =
+                mCarrierConfigTracker
+                        .getShowOperatorNameInStatusBarConfig(defaultSubInfo.getSubId())
+                        && (mTunerService.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0);
+        mView.update(showOperatorName, mTelephonyManager.isDataCapable(), getDefaultSubInfo());
     }
 
-    private List<SubInfo> getSubInfos() {
-        List<SubInfo> result = new ArrayList<>();
-        List<SubscriptionInfo> subscritionInfos =
-                mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
-
-        for (SubscriptionInfo subscriptionInfo : subscritionInfos) {
-            int subId = subscriptionInfo.getSubscriptionId();
-            result.add(new SubInfo(
-                    subscriptionInfo.getCarrierName(),
-                    mKeyguardUpdateMonitor.getSimState(subId),
-                    mKeyguardUpdateMonitor.getServiceState(subId)));
-        }
-
-        return result;
+    private SubInfo getDefaultSubInfo() {
+        int defaultSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+        SubscriptionInfo sI = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(defaultSubId);
+        return new SubInfo(
+                sI.getSubscriptionId(),
+                sI.getCarrierName(),
+                mKeyguardUpdateMonitor.getSimState(defaultSubId),
+                mKeyguardUpdateMonitor.getServiceState(defaultSubId));
     }
 
     /** Factory for constructing an {@link OperatorNameViewController}. */
@@ -106,22 +107,32 @@
         private final TunerService mTunerService;
         private final TelephonyManager mTelephonyManager;
         private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+        private final CarrierConfigTracker mCarrierConfigTracker;
 
         @Inject
-        public Factory(DarkIconDispatcher darkIconDispatcher, NetworkController networkController,
-                TunerService tunerService, TelephonyManager telephonyManager,
-                KeyguardUpdateMonitor keyguardUpdateMonitor) {
+        public Factory(DarkIconDispatcher darkIconDispatcher,
+                NetworkController networkController,
+                TunerService tunerService,
+                TelephonyManager telephonyManager,
+                KeyguardUpdateMonitor keyguardUpdateMonitor,
+                CarrierConfigTracker carrierConfigTracker) {
             mDarkIconDispatcher = darkIconDispatcher;
             mNetworkController = networkController;
             mTunerService = tunerService;
             mTelephonyManager = telephonyManager;
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+            mCarrierConfigTracker = carrierConfigTracker;
         }
 
         /** Create an {@link OperatorNameViewController}. */
         public OperatorNameViewController create(OperatorNameView view) {
-            return new OperatorNameViewController(view, mDarkIconDispatcher, mNetworkController,
-                    mTunerService, mTelephonyManager, mKeyguardUpdateMonitor);
+            return new OperatorNameViewController(view,
+                    mDarkIconDispatcher,
+                    mNetworkController,
+                    mTunerService,
+                    mTelephonyManager,
+                    mKeyguardUpdateMonitor,
+                    mCarrierConfigTracker);
         }
     }
 
@@ -152,7 +163,7 @@
             new KeyguardUpdateMonitorCallback() {
         @Override
         public void onRefreshCarrierInfo() {
-            mView.updateText(getSubInfos());
+            mView.updateText(getDefaultSubInfo());
         }
     };
 
@@ -176,17 +187,26 @@
     };
 
     static class SubInfo {
+        private final int mSubId;
         private final CharSequence mCarrierName;
         private final int mSimState;
         private final ServiceState mServiceState;
 
-        private SubInfo(CharSequence carrierName,
-                int simState, ServiceState serviceState) {
+        private SubInfo(
+                int subId,
+                CharSequence carrierName,
+                int simState,
+                ServiceState serviceState) {
+            mSubId = subId;
             mCarrierName = carrierName;
             mSimState = simState;
             mServiceState = serviceState;
         }
 
+        int getSubId() {
+            return mSubId;
+        }
+
         boolean simReady() {
             return mSimState == TelephonyManager.SIM_STATE_READY;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 6c6ec19..06532c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -61,6 +61,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.CarrierConfigTracker;
 import com.android.systemui.util.settings.SecureSettings;
 
 import java.util.concurrent.Executor;
@@ -263,6 +264,7 @@
             NetworkController networkController,
             StatusBarStateController statusBarStateController,
             CommandQueue commandQueue,
+            CarrierConfigTracker carrierConfigTracker,
             CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
             OperatorNameViewController.Factory operatorNameViewControllerFactory,
             SecureSettings secureSettings,
@@ -282,6 +284,7 @@
                 networkController,
                 statusBarStateController,
                 commandQueue,
+                carrierConfigTracker,
                 collapsedStatusBarFragmentLogger,
                 operatorNameViewControllerFactory,
                 secureSettings,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 8194957..9e48b76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -32,6 +32,7 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.provider.Settings;
+import android.telephony.SubscriptionManager;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -69,6 +70,9 @@
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.EncryptionHelper;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.CarrierConfigTracker;
+import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListener;
+import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
 import com.android.systemui.util.settings.SecureSettings;
 
 import java.util.ArrayList;
@@ -115,6 +119,7 @@
     private final NotificationIconAreaController mNotificationIconAreaController;
     private final PanelExpansionStateManager mPanelExpansionStateManager;
     private final StatusBarIconController mStatusBarIconController;
+    private final CarrierConfigTracker mCarrierConfigTracker;
     private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
     private final SecureSettings mSecureSettings;
     private final Executor mMainExecutor;
@@ -137,6 +142,28 @@
     private OperatorNameViewController mOperatorNameViewController;
     private StatusBarSystemEventAnimator mSystemEventAnimator;
 
+    private final CarrierConfigChangedListener mCarrierConfigCallback =
+            new CarrierConfigChangedListener() {
+                @Override
+                public void onCarrierConfigChanged() {
+                    if (mOperatorNameViewController == null) {
+                        initOperatorName();
+                    } else {
+                        // Already initialized, KeyguardUpdateMonitorCallback will handle the update
+                    }
+                }
+            };
+
+    private final DefaultDataSubscriptionChangedListener mDefaultDataListener =
+            new DefaultDataSubscriptionChangedListener() {
+                @Override
+                public void onDefaultSubscriptionChanged(int subId) {
+                    if (mOperatorNameViewController == null) {
+                        initOperatorName();
+                    }
+                }
+            };
+
     @SuppressLint("ValidFragment")
     public CollapsedStatusBarFragment(
             StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
@@ -153,6 +180,7 @@
             NetworkController networkController,
             StatusBarStateController statusBarStateController,
             CommandQueue commandQueue,
+            CarrierConfigTracker carrierConfigTracker,
             CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
             OperatorNameViewController.Factory operatorNameViewControllerFactory,
             SecureSettings secureSettings,
@@ -172,6 +200,7 @@
         mNetworkController = networkController;
         mStatusBarStateController = statusBarStateController;
         mCommandQueue = commandQueue;
+        mCarrierConfigTracker = carrierConfigTracker;
         mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
         mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
         mSecureSettings = secureSettings;
@@ -212,6 +241,8 @@
         initNotificationIconArea();
         mSystemEventAnimator =
                 new StatusBarSystemEventAnimator(mSystemIconArea, getResources());
+        mCarrierConfigTracker.addCallback(mCarrierConfigCallback);
+        mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
     }
 
     @VisibleForTesting
@@ -283,6 +314,8 @@
         if (mNetworkController.hasEmergencyCryptKeeperText()) {
             mNetworkController.removeCallback(mSignalCallback);
         }
+        mCarrierConfigTracker.removeCallback(mCarrierConfigCallback);
+        mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener);
     }
 
     /** Initializes views related to the notification icon area. */
@@ -569,11 +602,16 @@
     }
 
     private void initOperatorName() {
-        if (getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar)) {
+        int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+        if (mCarrierConfigTracker.getShowOperatorNameInStatusBarConfig(subId)) {
             ViewStub stub = mStatusBar.findViewById(R.id.operator_name);
             mOperatorNameViewController =
                     mOperatorNameViewControllerFactory.create((OperatorNameView) stub.inflate());
             mOperatorNameViewController.init();
+            // This view should not be visible on lock-screen
+            if (mKeyguardStateController.isShowing()) {
+                hideOperatorName(false);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 95a7316..ecaa28b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.policy;
 
 import android.annotation.Nullable;
+import android.view.View;
 
 import com.android.systemui.Dumpable;
 import com.android.systemui.demomode.DemoMode;
@@ -24,6 +25,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 
 public interface BatteryController extends DemoMode, Dumpable,
         CallbackController<BatteryStateChangeCallback> {
@@ -35,7 +37,32 @@
     /**
      * Sets if the current device is in power save mode.
      */
-    void setPowerSaveMode(boolean powerSave);
+    default void setPowerSaveMode(boolean powerSave) {
+        setPowerSaveMode(powerSave, null);
+    }
+
+    /**
+     * Sets if the current device is in power save mode.
+     *
+     * Can pass the view that triggered the request.
+     */
+    void setPowerSaveMode(boolean powerSave, @Nullable View view);
+
+    /**
+     * Gets a reference to the last view used when called {@link #setPowerSaveMode}.
+     */
+    @Nullable
+    default WeakReference<View> getLastPowerSaverStartView() {
+        return null;
+    }
+
+    /**
+     * Clears the last view used when called {@link #setPowerSaveMode}.
+     *
+     * Immediately after calling this, a call to {@link #getLastPowerSaverStartView()} should return
+     * {@code null}.
+     */
+    default void clearLastPowerSaverStartView() {}
 
     /**
      * Returns {@code true} if the device is currently plugged in.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 9e2c478..1e71dea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -28,6 +28,7 @@
 import android.os.PowerManager;
 import android.os.PowerSaveState;
 import android.util.Log;
+import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -45,8 +46,10 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Default implementation of a {@link BatteryController}. This controller monitors for battery
@@ -85,6 +88,11 @@
     private Estimate mEstimate;
     private boolean mFetchingEstimate = false;
 
+    // Use AtomicReference because we may request it from a different thread
+    // Use WeakReference because we are keeping a reference to a View that's not as long lived
+    // as this controller.
+    private AtomicReference<WeakReference<View>> mPowerSaverStartView = new AtomicReference<>();
+
     @VisibleForTesting
     public BatteryControllerImpl(
             Context context,
@@ -141,11 +149,22 @@
     }
 
     @Override
-    public void setPowerSaveMode(boolean powerSave) {
+    public void setPowerSaveMode(boolean powerSave, View view) {
+        if (powerSave) mPowerSaverStartView.set(new WeakReference<>(view));
         BatterySaverUtils.setPowerSaveMode(mContext, powerSave, /*needFirstTimeWarning*/ true);
     }
 
     @Override
+    public WeakReference<View> getLastPowerSaverStartView() {
+        return mPowerSaverStartView.get();
+    }
+
+    @Override
+    public void clearLastPowerSaverStartView() {
+        mPowerSaverStartView.set(null);
+    }
+
+    @Override
     public void addCallback(@NonNull BatteryController.BatteryStateChangeCallback cb) {
         synchronized (mChangeCallbacks) {
             mChangeCallbacks.add(cb);
diff --git a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
index 14190fa..5f7d745 100644
--- a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
@@ -23,43 +23,73 @@
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.util.ArraySet;
 import android.util.SparseBooleanArray;
 
+import androidx.annotation.NonNull;
+
+import com.android.internal.telephony.TelephonyIntents;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import java.util.Set;
 
 import javax.inject.Inject;
 
 /**
- * Tracks the Carrier Config values.
+ * Tracks CarrierConfigs for each subId, as well as the default configuration. CarrierConfigurations
+ * do not trigger a device configuration event, so any UI that relies on carrier configurations must
+ * register with the tracker to get proper updates.
+ *
+ * The tracker also listens for `TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED`
+ *
+ * @see CarrierConfigChangedListener to listen for updates
  */
 @SysUISingleton
-public class CarrierConfigTracker extends BroadcastReceiver {
+public class CarrierConfigTracker
+        extends BroadcastReceiver
+        implements CallbackController<CarrierConfigTracker.CarrierConfigChangedListener> {
     private final SparseBooleanArray mCallStrengthConfigs = new SparseBooleanArray();
     private final SparseBooleanArray mNoCallingConfigs = new SparseBooleanArray();
     private final SparseBooleanArray mCarrierProvisionsWifiMergedNetworks =
             new SparseBooleanArray();
+    private final SparseBooleanArray mShowOperatorNameConfigs = new SparseBooleanArray();
     private final CarrierConfigManager mCarrierConfigManager;
+    private final Set<CarrierConfigChangedListener> mListeners = new ArraySet<>();
+    private final Set<DefaultDataSubscriptionChangedListener> mDataListeners =
+            new ArraySet<>();
     private boolean mDefaultCallStrengthConfigLoaded;
     private boolean mDefaultCallStrengthConfig;
     private boolean mDefaultNoCallingConfigLoaded;
     private boolean mDefaultNoCallingConfig;
     private boolean mDefaultCarrierProvisionsWifiMergedNetworksLoaded;
     private boolean mDefaultCarrierProvisionsWifiMergedNetworks;
+    private boolean mDefaultShowOperatorNameConfigLoaded;
+    private boolean mDefaultShowOperatorNameConfig;
 
     @Inject
-    public CarrierConfigTracker(Context context, BroadcastDispatcher broadcastDispatcher) {
-        mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
-        broadcastDispatcher.registerReceiver(
-                this, new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+    public CarrierConfigTracker(
+            CarrierConfigManager carrierConfigManager,
+            BroadcastDispatcher broadcastDispatcher) {
+        mCarrierConfigManager = carrierConfigManager;
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+        broadcastDispatcher.registerReceiver(this, filter);
     }
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (!CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
-            return;
+        String action = intent.getAction();
+        if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) {
+            updateFromNewCarrierConfig(intent);
+        } else if (TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) {
+            updateDefaultDataSubscription(intent);
         }
+    }
 
+    private void updateFromNewCarrierConfig(Intent intent) {
         final int subId = intent.getIntExtra(
                 CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
@@ -84,6 +114,29 @@
             mCarrierProvisionsWifiMergedNetworks.put(subId, config.getBoolean(
                     CarrierConfigManager.KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL));
         }
+        synchronized (mShowOperatorNameConfigs) {
+            mShowOperatorNameConfigs.put(subId, config.getBoolean(
+                    CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL));
+        }
+
+        notifyCarrierConfigChanged();
+    }
+
+    private void updateDefaultDataSubscription(Intent intent) {
+        int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, -1);
+        notifyDefaultDataSubscriptionChanged(subId);
+    }
+
+    private void notifyCarrierConfigChanged() {
+        for (CarrierConfigChangedListener l : mListeners) {
+            l.onCarrierConfigChanged();
+        }
+    }
+
+    private void notifyDefaultDataSubscriptionChanged(int subId) {
+        for (DefaultDataSubscriptionChangedListener l : mDataListeners) {
+            l.onDefaultSubscriptionChanged(subId);
+        }
     }
 
     /**
@@ -139,4 +192,73 @@
         }
         return mDefaultCarrierProvisionsWifiMergedNetworks;
     }
+
+    /**
+     * Returns the KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL value for the default config
+     */
+    public boolean getShowOperatorNameInStatusBarConfigDefault() {
+        if (!mDefaultShowOperatorNameConfigLoaded) {
+            mDefaultShowOperatorNameConfig = CarrierConfigManager.getDefaultConfig().getBoolean(
+                    CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL);
+            mDefaultShowOperatorNameConfigLoaded = true;
+        }
+
+        return mDefaultShowOperatorNameConfig;
+    }
+
+    /**
+     * Returns the KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL value for the given subId, or the
+     * default value if no override exists
+     *
+     * @param subId the subscription id for which to query the config
+     */
+    public boolean getShowOperatorNameInStatusBarConfig(int subId) {
+        if (mShowOperatorNameConfigs.indexOfKey(subId) >= 0) {
+            return mShowOperatorNameConfigs.get(subId);
+        } else {
+            return getShowOperatorNameInStatusBarConfigDefault();
+        }
+    }
+
+    @Override
+    public void addCallback(@NonNull CarrierConfigChangedListener listener) {
+        mListeners.add(listener);
+    }
+
+    @Override
+    public void removeCallback(@NonNull CarrierConfigChangedListener listener) {
+        mListeners.remove(listener);
+    }
+
+    /** */
+    public void addDefaultDataSubscriptionChangedListener(
+            @NonNull DefaultDataSubscriptionChangedListener listener) {
+        mDataListeners.add(listener);
+    }
+
+    /** */
+    public void removeDataSubscriptionChangedListener(
+            DefaultDataSubscriptionChangedListener listener) {
+        mDataListeners.remove(listener);
+    }
+
+    /**
+     * Called when carrier config changes
+     */
+    public interface CarrierConfigChangedListener {
+        /** */
+        void onCarrierConfigChanged();
+    }
+
+    /**
+     * Called when the default data subscription changes. Listeners may want to query
+     * subId-dependent configuration values when this event happens
+     */
+    public interface DefaultDataSubscriptionChangedListener {
+        /**
+         * @param subId the new default data subscription id per
+         * {@link SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX}
+         */
+        void onDefaultSubscriptionChanged(int subId);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 9116716..538a9c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -105,6 +105,7 @@
     @Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory
     @Mock private lateinit var mediaCarouselController: MediaCarouselController
     @Mock private lateinit var falsingManager: FalsingManager
+    @Mock private lateinit var transitionParent: ViewGroup
     private lateinit var appIcon: ImageView
     private lateinit var albumView: ImageView
     private lateinit var titleText: TextView
@@ -242,6 +243,10 @@
         whenever(viewHolder.seamlessText).thenReturn(seamlessText)
         whenever(viewHolder.seekBar).thenReturn(seekBar)
 
+        // Transition View
+        whenever(view.parent).thenReturn(transitionParent)
+        whenever(view.rootView).thenReturn(transitionParent)
+
         // Action buttons
         whenever(viewHolder.actionPlayPause).thenReturn(actionPlayPause)
         whenever(viewHolder.getAction(R.id.actionPlayPause)).thenReturn(actionPlayPause)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index ef53154..9a01464 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -277,6 +277,17 @@
     }
 
     @Test
+    fun commandQueueCallback_invalidStateParam_noChipShown() {
+        commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+            100,
+            routeInfo,
+            null
+        )
+
+        verify(windowManager, never()).addView(any(), any())
+    }
+
+    @Test
     fun receivesNewStateFromCommandQueue_isLogged() {
         commandQueueCallback.updateMediaTapToTransferSenderDisplay(
             StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index a156820..1ffa9dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -25,29 +25,48 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyString;
 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 static org.mockito.Mockito.when;
 
 import android.app.Notification;
 import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.BatteryManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
 import android.test.suitebuilder.annotation.SmallTest;
-
-import androidx.test.runner.AndroidJUnit4;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.settingslib.fuelgauge.BatterySaverUtils;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.NotificationChannels;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.ref.WeakReference;
 
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class PowerNotificationWarningsTest extends SysuiTestCase {
 
     public static final String FORMATTED_45M = "0h 45m";
@@ -55,14 +74,34 @@
     private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
     private PowerNotificationWarnings mPowerNotificationWarnings;
 
+    @Mock
+    private BatteryController mBatteryController;
+    @Mock
+    private DialogLaunchAnimator mDialogLaunchAnimator;
+    @Mock
+    private View mView;
+
+    private BroadcastReceiver mReceiver;
+
     @Before
     public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        Context wrapper = new ContextWrapper(mContext) {
+            @Override
+            public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+                    IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
+                mReceiver = receiver;
+                return null;
+            }
+        };
+
         // Test Instance.
         mContext.addMockSystemService(NotificationManager.class, mMockNotificationManager);
         ActivityStarter starter = mDependency.injectMockDependency(ActivityStarter.class);
         BroadcastSender broadcastSender = mDependency.injectMockDependency(BroadcastSender.class);
-        mPowerNotificationWarnings = new PowerNotificationWarnings(mContext, starter,
-                broadcastSender);
+        mPowerNotificationWarnings = new PowerNotificationWarnings(wrapper, starter,
+                broadcastSender, () -> mBatteryController, mDialogLaunchAnimator);
         BatteryStateSnapshot snapshot = new BatteryStateSnapshot(100, false, false, 1,
                 BatteryManager.BATTERY_HEALTH_GOOD, 5, 15);
         mPowerNotificationWarnings.updateSnapshot(snapshot);
@@ -168,4 +207,52 @@
 
         mPowerNotificationWarnings.mUsbHighTempDialog.dismiss();
     }
+
+    @Test
+    public void testDialogStartedFromLauncher_viewVisible() {
+        when(mBatteryController.getLastPowerSaverStartView())
+                .thenReturn(new WeakReference<>(mView));
+        when(mView.isAggregatedVisible()).thenReturn(true);
+
+        Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION);
+        intent.putExtras(new Bundle());
+
+        mReceiver.onReceive(mContext, intent);
+
+        verify(mDialogLaunchAnimator).showFromView(any(), eq(mView));
+
+        mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
+    }
+
+    @Test
+    public void testDialogStartedNotFromLauncher_viewNotVisible() {
+        when(mBatteryController.getLastPowerSaverStartView())
+                .thenReturn(new WeakReference<>(mView));
+        when(mView.isAggregatedVisible()).thenReturn(false);
+
+        Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION);
+        intent.putExtras(new Bundle());
+
+        mReceiver.onReceive(mContext, intent);
+
+        verify(mDialogLaunchAnimator, never()).showFromView(any(), any());
+
+        assertThat(mPowerNotificationWarnings.getSaverConfirmationDialog().isShowing()).isTrue();
+        mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
+    }
+
+    @Test
+    public void testDialogShownNotFromLauncher() {
+        when(mBatteryController.getLastPowerSaverStartView()).thenReturn(null);
+
+        Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION);
+        intent.putExtras(new Bundle());
+
+        mReceiver.onReceive(mContext, intent);
+
+        verify(mDialogLaunchAnimator, never()).showFromView(any(), any());
+
+        assertThat(mPowerNotificationWarnings.getSaverConfirmationDialog().isShowing()).isTrue();
+        mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index 1bf8351..3d9205e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -21,6 +21,7 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
+import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
@@ -38,6 +39,9 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @RunWith(AndroidTestingRunner::class)
@@ -63,6 +67,8 @@
     private lateinit var qsLogger: QSLogger
     @Mock
     private lateinit var batteryController: BatteryController
+    @Mock
+    private lateinit var view: View
     private lateinit var secureSettings: SecureSettings
     private lateinit var testableLooper: TestableLooper
     private lateinit var tile: BatterySaverTile
@@ -105,4 +111,26 @@
 
         assertEquals(USER + 1, tile.mSetting.currentUser)
     }
+
+    @Test
+    fun testClickingPowerSavePassesView() {
+        tile.onPowerSaveChanged(true)
+        tile.handleClick(view)
+
+        tile.onPowerSaveChanged(false)
+        tile.handleClick(view)
+
+        verify(batteryController).setPowerSaveMode(true, view)
+        verify(batteryController).setPowerSaveMode(false, view)
+    }
+
+    @Test
+    fun testStopListeningClearsViewInController() {
+        clearInvocations(batteryController)
+        tile.handleSetListening(true)
+        verify(batteryController, never()).clearLastPowerSaverStartView()
+
+        tile.handleSetListening(false)
+        verify(batteryController).clearLastPowerSaverStartView()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 48f8206..7687d12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -66,6 +66,7 @@
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.FakeSettings;
 
 import com.google.android.collect.Lists;
 
@@ -109,6 +110,7 @@
     private UserInfo mCurrentUser;
     private UserInfo mSecondaryUser;
     private UserInfo mWorkUser;
+    private FakeSettings mSettings;
     private TestNotificationLockscreenUserManager mLockscreenUserManager;
     private NotificationEntry mCurrentUserNotif;
     private NotificationEntry mSecondaryUserNotif;
@@ -120,6 +122,8 @@
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
 
         int currentUserId = ActivityManager.getCurrentUser();
+        mSettings = new FakeSettings();
+        mSettings.setUserId(ActivityManager.getCurrentUser());
         mCurrentUser = new UserInfo(currentUserId, "", 0);
         mSecondaryUser = new UserInfo(currentUserId + 1, "", 0);
         mWorkUser = new UserInfo(currentUserId + 2, "" /* name */, null /* iconPath */, 0,
@@ -157,48 +161,45 @@
 
     @Test
     public void testLockScreenShowNotificationsFalse() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications());
     }
 
     @Test
     public void testLockScreenShowNotificationsTrue() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications());
     }
 
     @Test
     public void testLockScreenAllowPrivateNotificationsTrue() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
     }
 
     @Test
     public void testLockScreenAllowPrivateNotificationsFalse() {
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mCurrentUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
     }
 
     @Test
     public void testLockScreenAllowsWorkPrivateNotificationsFalse() {
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mWorkUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
     }
 
     @Test
     public void testLockScreenAllowsWorkPrivateNotificationsTrue() {
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mWorkUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
     }
@@ -206,8 +207,8 @@
     @Test
     public void testCurrentUserPrivateNotificationsNotRedacted() {
         // GIVEN current user doesn't allow private notifications to show
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mCurrentUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         // THEN current user's notification is redacted
@@ -217,8 +218,8 @@
     @Test
     public void testCurrentUserPrivateNotificationsRedacted() {
         // GIVEN current user allows private notifications to show
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mCurrentUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         // THEN current user's notification isn't redacted
@@ -228,8 +229,8 @@
     @Test
     public void testWorkPrivateNotificationsRedacted() {
         // GIVEN work profile doesn't private notifications to show
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mWorkUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         // THEN work profile notification is redacted
@@ -239,8 +240,8 @@
     @Test
     public void testWorkPrivateNotificationsNotRedacted() {
         // GIVEN work profile allows private notifications to show
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mWorkUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         // THEN work profile notification isn't redacted
@@ -250,12 +251,11 @@
     @Test
     public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() {
         // GIVEN work profile allows private notifications to show but the other users don't
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id);
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mWorkUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mCurrentUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
                 mSecondaryUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
@@ -270,12 +270,11 @@
     @Test
     public void testWorkProfileRedacted_otherUsersNotRedacted() {
         // GIVEN work profile doesn't allow private notifications to show but the other users do
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id);
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id);
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mWorkUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mCurrentUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
                 mSecondaryUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
@@ -291,10 +290,9 @@
     public void testSecondaryUserNotRedacted_currentUserRedacted() {
         // GIVEN secondary profile allows private notifications to show but the current user
         // doesn't allow private notifications to show
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
-        Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mCurrentUser.id);
+        mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
                 mSecondaryUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
@@ -328,10 +326,9 @@
 
     @Test
     public void testShowSilentNotifications_settingSaysShow() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         NotificationEntry entry = new NotificationEntryBuilder()
                 .setImportance(IMPORTANCE_LOW)
@@ -343,10 +340,9 @@
 
     @Test
     public void testShowSilentNotifications_settingSaysHide() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         final Notification notification = mock(Notification.class);
         when(notification.isForegroundService()).thenReturn(true);
@@ -360,10 +356,9 @@
 
     @Test
     public void testShowSilentNotificationsPeopleBucket_settingSaysHide() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         final Notification notification = mock(Notification.class);
         when(notification.isForegroundService()).thenReturn(true);
@@ -377,10 +372,9 @@
 
     @Test
     public void testShowSilentNotificationsMediaBucket_settingSaysHide() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
 
         final Notification notification = mock(Notification.class);
         when(notification.isForegroundService()).thenReturn(true);
@@ -396,8 +390,8 @@
     @Test
     public void testKeyguardNotificationSuppressors() {
         // GIVEN a notification that should be shown on the lockscreen
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
         final NotificationEntry entry = new NotificationEntryBuilder()
                 .setImportance(IMPORTANCE_HIGH)
                 .build();
@@ -433,6 +427,7 @@
                     Handler.createAsync(Looper.myLooper()),
                     mDeviceProvisionedController,
                     mKeyguardStateController,
+                    mSettings,
                     mock(DumpManager.class));
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 5095094..497f7fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -59,6 +59,7 @@
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.CarrierConfigTracker;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -90,6 +91,7 @@
     private OperatorNameViewController mOperatorNameViewController;
     private SecureSettings mSecureSettings;
     private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+    private final CarrierConfigTracker mCarrierConfigTracker = mock(CarrierConfigTracker.class);
 
     @Mock
     private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
@@ -373,6 +375,7 @@
                 mNetworkController,
                 mStatusBarStateController,
                 mCommandQueue,
+                mCarrierConfigTracker,
                 new CollapsedStatusBarFragmentLogger(
                         new LogBuffer("TEST", 1, 1, mock(LogcatEchoTracker.class)),
                         new DisableFlagsLogger()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index 2577dbd..b714df5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -18,6 +18,10 @@
 
 import static android.os.BatteryManager.EXTRA_PRESENT;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker;
+
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -30,20 +34,24 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.view.View;
 
+import com.android.dx.mockito.inline.extended.StaticInOrder;
+import com.android.settingslib.fuelgauge.BatterySaverUtils;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-
+import org.mockito.MockitoSession;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -53,11 +61,19 @@
     @Mock private PowerManager mPowerManager;
     @Mock private BroadcastDispatcher mBroadcastDispatcher;
     @Mock private DemoModeController mDemoModeController;
+    @Mock private View mView;
     private BatteryControllerImpl mBatteryController;
 
+    private MockitoSession mMockitoSession;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mMockitoSession = mockitoSession()
+                .initMocks(this)
+                .mockStatic(BatterySaverUtils.class)
+                .startMocking();
+
         mBatteryController = new BatteryControllerImpl(getContext(),
                 mock(EnhancedEstimates.class),
                 mPowerManager,
@@ -68,6 +84,11 @@
         mBatteryController.init();
     }
 
+    @After
+    public void tearDown() {
+        mMockitoSession.finishMocking();
+    }
+
     @Test
     public void testBatteryInitialized() {
         Assert.assertTrue(mBatteryController.mHasReceivedBattery);
@@ -135,4 +156,33 @@
         // THEN it is informed about the battery state
         verify(cb, atLeastOnce()).onBatteryUnknownStateChanged(true);
     }
+
+    @Test
+    public void testBatteryUtilsCalledOnSetPowerSaveMode() {
+        mBatteryController.setPowerSaveMode(true, mView);
+        mBatteryController.setPowerSaveMode(false, mView);
+
+        StaticInOrder inOrder = inOrder(staticMockMarker(BatterySaverUtils.class));
+        inOrder.verify(() -> BatterySaverUtils.setPowerSaveMode(getContext(), true, true));
+        inOrder.verify(() -> BatterySaverUtils.setPowerSaveMode(getContext(), false, true));
+    }
+
+    @Test
+    public void testSaveViewReferenceWhenSettingPowerSaveMode() {
+        mBatteryController.setPowerSaveMode(false, mView);
+
+        Assert.assertNull(mBatteryController.getLastPowerSaverStartView());
+
+        mBatteryController.setPowerSaveMode(true, mView);
+
+        Assert.assertSame(mView, mBatteryController.getLastPowerSaverStartView().get());
+    }
+
+    @Test
+    public void testClearViewReference() {
+        mBatteryController.setPowerSaveMode(true, mView);
+        mBatteryController.clearLastPowerSaverStartView();
+
+        Assert.assertNull(mBatteryController.getLastPowerSaverStartView());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
index e66491e..e660e1f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.util.settings;
 
+import android.annotation.UserIdInt;
 import android.content.ContentResolver;
 import android.database.ContentObserver;
 import android.net.Uri;
@@ -34,6 +35,8 @@
     private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>();
 
     public static final Uri CONTENT_URI = Uri.parse("content://settings/fake");
+    @UserIdInt
+    private int mUserId = UserHandle.USER_CURRENT;
 
     public FakeSettings() {
     }
@@ -85,9 +88,13 @@
         return Uri.withAppendedPath(CONTENT_URI, name);
     }
 
+    public void setUserId(@UserIdInt int userId) {
+        mUserId = userId;
+    }
+
     @Override
     public int getUserId() {
-        return UserHandle.USER_CURRENT;
+        return mUserId;
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
index 50c1e73..9ca4db4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -16,6 +16,7 @@
 
 import android.os.Bundle;
 import android.testing.LeakCheck;
+import android.view.View;
 
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -47,6 +48,11 @@
     }
 
     @Override
+    public void setPowerSaveMode(boolean powerSave, View view) {
+
+    }
+
+    @Override
     public boolean isPluggedIn() {
         return false;
     }
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 76df8b9..e78c8d1 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -24,8 +24,10 @@
 import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 
+import android.annotation.NonNull;
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
+import android.app.backup.BackupAgent;
 import android.app.backup.BackupManager;
 import android.app.backup.FullBackup;
 import android.app.backup.IBackupManagerMonitor;
@@ -38,10 +40,12 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.provider.Settings;
+import android.system.OsConstants;
 import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
@@ -57,6 +61,7 @@
 import com.android.server.backup.utils.RestoreUtils;
 import com.android.server.backup.utils.TarBackupReader;
 
+import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -135,6 +140,8 @@
     private boolean mPipesClosed;
     private final BackupEligibilityRules mBackupEligibilityRules;
 
+    private FileMetadata mReadOnlyParent = null;
+
     public FullRestoreEngine(
             UserBackupManagerService backupManagerService, OperationStorage operationStorage,
             BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
@@ -158,6 +165,22 @@
         mBackupEligibilityRules = backupEligibilityRules;
     }
 
+    @VisibleForTesting
+    FullRestoreEngine() {
+        mIsAdbRestore = false;
+        mAllowApks = false;
+        mEphemeralOpToken = 0;
+        mUserId = 0;
+        mBackupEligibilityRules = null;
+        mAgentTimeoutParameters = null;
+        mBuffer = null;
+        mBackupManagerService = null;
+        mOperationStorage = null;
+        mMonitor = null;
+        mMonitorTask = null;
+        mOnlyPackage = null;
+    }
+
     public IBackupAgent getAgent() {
         return mAgent;
     }
@@ -397,6 +420,11 @@
                         okay = false;
                     }
 
+                    if (shouldSkipReadOnlyDir(info)) {
+                        // b/194894879: We don't support restore of read-only dirs.
+                        okay = false;
+                    }
+
                     // At this point we have an agent ready to handle the full
                     // restore data as well as a pipe for sending data to
                     // that agent.  Tell the agent to start reading from the
@@ -573,6 +601,45 @@
         return (info != null);
     }
 
+    boolean shouldSkipReadOnlyDir(FileMetadata info) {
+        if (isValidParent(mReadOnlyParent, info)) {
+            // This file has a read-only parent directory, we shouldn't
+            // restore it.
+            return true;
+        } else {
+            // We're now in a different branch of the file tree, update the parent
+            // value.
+            if (isReadOnlyDir(info)) {
+                // Current directory is read-only. Remember it so that we can skip all
+                // of its contents.
+                mReadOnlyParent = info;
+                Slog.w(TAG, "Skipping restore of " + info.path + " and its contents as "
+                        + "read-only dirs are currently not supported.");
+                return true;
+            } else {
+                mReadOnlyParent = null;
+            }
+        }
+
+        return false;
+    }
+
+    private static boolean isValidParent(FileMetadata parentDir, @NonNull FileMetadata childDir) {
+        return parentDir != null
+                && childDir.packageName.equals(parentDir.packageName)
+                && childDir.domain.equals(parentDir.domain)
+                && childDir.path.startsWith(getPathWithTrailingSeparator(parentDir.path));
+    }
+
+    private static String getPathWithTrailingSeparator(String path) {
+        return path.endsWith(File.separator) ? path : path + File.separator;
+    }
+
+    private static boolean isReadOnlyDir(FileMetadata file) {
+        // Check if owner has 'write' bit in the file's mode value (see 'man -7 inode' for details).
+        return file.type == BackupAgent.TYPE_DIRECTORY && (file.mode & OsConstants.S_IWUSR) == 0;
+    }
+
     private void setUpPipes() throws IOException {
         synchronized (mPipesLock) {
             mPipes = ParcelFileDescriptor.createPipe();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index e6bf109..7ab3008 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2132,15 +2132,19 @@
             }
         }
 
-        PackageMonitor monitor = new PackageMonitor() {
+        if (mPackageMonitorsForUser.get(userId) == null) {
+            PackageMonitor monitor = new PackageMonitor() {
                 @Override
                 public void onPackageRemoved(String packageName, int uid) {
                     updateLegacyStorageApps(packageName, uid, false);
                 }
             };
-        // TODO(b/149391976): Use different handler?
-        monitor.register(mContext, user, true, mHandler);
-        mPackageMonitorsForUser.put(userId, monitor);
+            // TODO(b/149391976): Use different handler?
+            monitor.register(mContext, user, true, mHandler);
+            mPackageMonitorsForUser.put(userId, monitor);
+        } else {
+            Slog.w(TAG, "PackageMonitor is already registered for: " + userId);
+        }
     }
 
     private static long getLastAccessTime(AppOpsManager manager,
diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
index 8be90e0c..b45bfb1 100644
--- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
+++ b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
@@ -30,6 +30,7 @@
 import android.os.UserHandle;
 import android.os.logcat.ILogcatManagerService;
 import android.util.Slog;
+import android.view.InflateException;
 import android.view.View;
 import android.widget.Button;
 import android.widget.TextView;
@@ -56,33 +57,46 @@
     private String mAlertTitle;
     private AlertDialog.Builder mAlertDialog;
     private AlertDialog mAlert;
+    private View mAlertView;
 
     private static final int DIALOG_TIME_OUT = Build.IS_DEBUGGABLE ? 60000 : 300000;
     private static final int MSG_DISMISS_DIALOG = 0;
 
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mContext = this;
 
-        Intent intent = getIntent();
-        mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
-        mUid = intent.getIntExtra("com.android.server.logcat.uid", 0);
-        mGid = intent.getIntExtra("com.android.server.logcat.gid", 0);
-        mPid = intent.getIntExtra("com.android.server.logcat.pid", 0);
-        mFd = intent.getIntExtra("com.android.server.logcat.fd", 0);
-        mAlertTitle = getTitleString(mContext, mPackageName, mUid);
+        try {
+            mContext = this;
 
-        if (mAlertTitle != null) {
+            // retrieve Intent extra information
+            Intent intent = getIntent();
+            getIntentInfo(intent);
 
+            // retrieve the title string from passed intent extra
+            mAlertTitle = getTitleString(mContext, mPackageName, mUid);
+
+            // creaet View
+            mAlertView = createView();
+
+            // create AlertDialog
             mAlertDialog = new AlertDialog.Builder(this);
-            mAlertDialog.setView(createView());
+            mAlertDialog.setView(mAlertView);
 
+            // show Alert
             mAlert = mAlertDialog.create();
             mAlert.show();
+
+            // set Alert Timeout
             mHandler.sendEmptyMessageDelayed(MSG_DISMISS_DIALOG, DIALOG_TIME_OUT);
 
+        } catch (Exception e) {
+            try {
+                Slog.e(TAG, "onCreate failed, declining the logd access", e);
+                mLogcatManagerService.decline(mUid, mGid, mPid, mFd);
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "Fails to call remote functions", ex);
+            }
         }
     }
 
@@ -95,6 +109,19 @@
         mAlert = null;
     }
 
+    private void getIntentInfo(Intent intent) throws Exception {
+
+        if (intent == null) {
+            throw new NullPointerException("Intent is null");
+        }
+
+        mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+        mUid = intent.getIntExtra("com.android.server.logcat.uid", 0);
+        mGid = intent.getIntExtra("com.android.server.logcat.gid", 0);
+        mPid = intent.getIntExtra("com.android.server.logcat.pid", 0);
+        mFd = intent.getIntExtra("com.android.server.logcat.fd", 0);
+    }
+
     private Handler mHandler = new Handler() {
         public void handleMessage(android.os.Message msg) {
             switch (msg.what) {
@@ -116,26 +143,41 @@
         }
     };
 
-    private String getTitleString(Context context, String callingPackage, int uid) {
+    private String getTitleString(Context context, String callingPackage, int uid)
+            throws Exception {
+
         PackageManager pm = context.getPackageManager();
-        try {
-            return context.getString(
-                    com.android.internal.R.string.log_access_confirmation_title,
-                    pm.getApplicationInfoAsUser(callingPackage,
-                            PackageManager.MATCH_DIRECT_BOOT_AUTO,
-                            UserHandle.getUserId(uid)).loadLabel(pm));
-        } catch (NameNotFoundException e) {
-            Slog.e(TAG, "App name is unknown.", e);
-            return null;
+        if (pm == null) {
+            throw new NullPointerException("PackageManager is null");
         }
+
+        CharSequence appLabel = pm.getApplicationInfoAsUser(callingPackage,
+                PackageManager.MATCH_DIRECT_BOOT_AUTO,
+                UserHandle.getUserId(uid)).loadLabel(pm);
+        if (appLabel == null) {
+            throw new NameNotFoundException("Application Label is null");
+        }
+
+        return context.getString(com.android.internal.R.string.log_access_confirmation_title,
+            appLabel);
     }
 
-    private View createView() {
+    /**
+     * Returns the dialog view.
+     * If we cannot retrieve the package name, it returns null and we decline the full device log
+     * access
+     */
+    private View createView() throws Exception {
+
         final View view = getLayoutInflater().inflate(
                 R.layout.log_access_user_consent_dialog_permission, null /*root*/);
 
+        if (view == null) {
+            throw new InflateException();
+        }
+
         ((TextView) view.findViewById(R.id.log_access_dialog_title))
-                .setText(mAlertTitle);
+            .setText(mAlertTitle);
 
         Button button_allow = (Button) view.findViewById(R.id.log_access_dialog_allow_button);
         button_allow.setOnClickListener(this);
@@ -144,6 +186,7 @@
         button_deny.setOnClickListener(this);
 
         return view;
+
     }
 
     @Override
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
index 015bf3c5..4c265ad 100644
--- a/services/core/java/com/android/server/logcat/LogcatManagerService.java
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -102,16 +102,27 @@
         }
     }
 
-    private void showDialog(int uid, int gid, int pid, int fd) {
+    /**
+     * Returns the package name.
+     * If we cannot retrieve the package name, it returns null and we decline the full device log
+     * access
+     */
+    private String getPackageName(int uid, int gid, int pid, int fd) {
+
         final ActivityManagerInternal activityManagerInternal =
                 LocalServices.getService(ActivityManagerInternal.class);
+        if (activityManagerInternal != null) {
+            String packageName = activityManagerInternal.getPackageNameByPid(pid);
+            if (packageName != null) {
+                return packageName;
+            }
+        }
 
         PackageManager pm = mContext.getPackageManager();
-        String packageName = activityManagerInternal.getPackageNameByPid(pid);
-        if (packageName != null) {
-            Intent mIntent = createIntent(packageName, uid, gid, pid, fd);
-            mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM);
-            return;
+        if (pm == null) {
+            // Decline the logd access if PackageManager is null
+            Slog.e(TAG, "PackageManager is null, declining the logd access");
+            return null;
         }
 
         String[] packageNames = pm.getPackagesForUid(uid);
@@ -119,21 +130,19 @@
         if (ArrayUtils.isEmpty(packageNames)) {
             // Decline the logd access if the app name is unknown
             Slog.e(TAG, "Unknown calling package name, declining the logd access");
-            declineLogdAccess(uid, gid, pid, fd);
-            return;
+            return null;
         }
 
         String firstPackageName = packageNames[0];
 
-        if (firstPackageName.isEmpty() || firstPackageName == null) {
+        if (firstPackageName == null || firstPackageName.isEmpty()) {
             // Decline the logd access if the package name from uid is unknown
             Slog.e(TAG, "Unknown calling package name, declining the logd access");
-            declineLogdAccess(uid, gid, pid, fd);
-            return;
+            return null;
         }
 
-        final Intent mIntent = createIntent(firstPackageName, uid, gid, pid, fd);
-        mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM);
+        return firstPackageName;
+
     }
 
     private void declineLogdAccess(int uid, int gid, int pid, int fd) {
@@ -198,16 +207,23 @@
 
                 final int procState = LocalServices.getService(ActivityManagerInternal.class)
                         .getUidProcessState(mUid);
-                // If the process is foreground, show a dialog for user consent
+                // If the process is foreground and we can retrieve the package name, show a dialog
+                // for user consent
                 if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
-                    showDialog(mUid, mGid, mPid, mFd);
-                } else {
-                    /**
-                     * If the process is background, decline the logd access.
-                     **/
-                    declineLogdAccess(mUid, mGid, mPid, mFd);
-                    return;
+                    String packageName = getPackageName(mUid, mGid, mPid, mFd);
+                    if (packageName != null) {
+                        final Intent mIntent = createIntent(packageName, mUid, mGid, mPid, mFd);
+                        mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM);
+                        return;
+                    }
                 }
+
+                /**
+                 * If the process is background or cannot retrieve the package name,
+                 * decline the logd access.
+                 **/
+                declineLogdAccess(mUid, mGid, mPid, mFd);
+                return;
             }
         }
     }
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 728782c..d0651ed 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -133,6 +133,10 @@
                 mIntentFilter, null, null);
     }
 
+    public void stop() {
+        mContext.unregisterReceiver(mBroadcastReceiver);
+    }
+
     /**
      * Transfers to a given bluetooth route.
      * The dedicated BT device with the route would be activated.
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index b307266..e27cbea 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -1150,6 +1150,8 @@
             if (DEBUG) {
                 Slog.d(TAG, userRecord + ": Disposed");
             }
+            userRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::stop, userRecord.mHandler));
             mUserRecords.remove(userRecord.mUserId);
             // Note: User already stopped (by switchUser) so no need to send stop message here.
         }
@@ -1330,6 +1332,7 @@
         private void start() {
             if (!mRunning) {
                 mRunning = true;
+                mSystemProvider.start();
                 mWatcher.start();
             }
         }
@@ -1338,6 +1341,7 @@
             if (mRunning) {
                 mRunning = false;
                 mWatcher.stop(); // also stops all providers
+                mSystemProvider.stop();
             }
         }
 
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 7878159..d91bf8c 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -71,6 +71,7 @@
     private final IAudioService mAudioService;
     private final Handler mHandler;
     private final Context mContext;
+    private final UserHandle mUser;
     private final BluetoothRouteProvider mBtRouteProvider;
 
     private static ComponentName sComponentName = new ComponentName(
@@ -86,6 +87,9 @@
     final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
     int mDeviceVolume;
 
+    private final AudioManagerBroadcastReceiver mAudioReceiver =
+            new AudioManagerBroadcastReceiver();
+
     private final Object mRequestLock = new Object();
     @GuardedBy("mRequestLock")
     private volatile SessionCreationRequest mPendingSessionCreationRequest;
@@ -108,6 +112,7 @@
 
         mIsSystemRouteProvider = true;
         mContext = context;
+        mUser = user;
         mHandler = new Handler(Looper.getMainLooper());
 
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -128,21 +133,33 @@
             }
         });
         updateSessionInfosIfNeeded();
+    }
 
+    public void start() {
         IntentFilter intentFilter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
         intentFilter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
-        mContext.registerReceiverAsUser(new AudioManagerBroadcastReceiver(), user,
+        mContext.registerReceiverAsUser(mAudioReceiver, mUser,
                 intentFilter, null, null);
 
         if (mBtRouteProvider != null) {
             mHandler.post(() -> {
-                mBtRouteProvider.start(user);
+                mBtRouteProvider.start(mUser);
                 notifyProviderState();
             });
         }
         updateVolume();
     }
 
+    public void stop() {
+        mContext.unregisterReceiver(mAudioReceiver);
+        if (mBtRouteProvider != null) {
+            mHandler.post(() -> {
+                mBtRouteProvider.stop();
+                notifyProviderState();
+            });
+        }
+    }
+
     @Override
     public void setCallback(Callback callback) {
         super.setCallback(callback);
diff --git a/services/core/java/com/android/server/net/TEST_MAPPING b/services/core/java/com/android/server/net/TEST_MAPPING
index 02095eb..4ccf09e 100644
--- a/services/core/java/com/android/server/net/TEST_MAPPING
+++ b/services/core/java/com/android/server/net/TEST_MAPPING
@@ -2,12 +2,8 @@
   "presubmit-large": [
     {
       "name": "CtsHostsideNetworkTests",
-      "file_patterns": ["(/|^)NetworkPolicy[^/]*\\.java"],
       "options": [
         {
-          "include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests"
-        },
-        {
           "exclude-annotation": "androidx.test.filters.FlakyTest"
         },
         {
diff --git a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
index 0418afb..1a2ff26 100644
--- a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
@@ -19,6 +19,7 @@
 import android.util.ArrayMap;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.UnboundedSdkLevel;
 import com.android.server.SystemConfig;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
 
@@ -51,8 +52,11 @@
 
     private void updateSharedLibraryForPackage(SystemConfig.SharedLibraryEntry entry,
             ParsedPackage parsedPackage) {
-        if (entry.onBootclasspathBefore != 0
-                && parsedPackage.getTargetSdkVersion() < entry.onBootclasspathBefore) {
+        if (entry.onBootclasspathBefore != null
+                && isTargetSdkAtMost(
+                        parsedPackage.getTargetSdkVersion(),
+                        entry.onBootclasspathBefore)
+                && UnboundedSdkLevel.isAtLeast(entry.onBootclasspathBefore)) {
             // this package targets an API where this library was in the BCP, so add
             // the library transparently in case the package is using it
             prefixRequiredLibrary(parsedPackage, entry.name);
@@ -64,4 +68,19 @@
             removeLibrary(parsedPackage, entry.name);
         }
     }
+
+    private static boolean isTargetSdkAtMost(int targetSdk, String onBcpBefore) {
+        if (isCodename(onBcpBefore)) {
+            return targetSdk < 10000;
+        }
+        return targetSdk < Integer.parseInt(onBcpBefore);
+    }
+
+    private static boolean isCodename(String version) {
+        if (version.length() == 0) {
+            throw new IllegalArgumentException();
+        }
+        // assume Android codenames start with upper case letters.
+        return Character.isUpperCase((version.charAt(0)));
+    }
 }
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 8ecc51b..3e36431 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -66,6 +66,7 @@
         IGNORED_FOR_ONGOING,
         IGNORED_FOR_POWER,
         IGNORED_FOR_RINGER_MODE,
+        IGNORED_FOR_RINGTONE,
         IGNORED_FOR_SETTINGS,
         IGNORED_SUPERSEDED,
     }
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index bf32985..d7341cb 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -733,6 +733,12 @@
                             + attrs);
                 }
                 break;
+            case IGNORED_FOR_RINGTONE:
+                if (DEBUG) {
+                    Slog.d(TAG, "Ignoring incoming vibration in favor of ringtone vibration");
+                }
+                break;
+
             default:
                 if (DEBUG) {
                     Slog.d(TAG, "Vibration for uid=" + uid + " and with attrs=" + attrs
@@ -809,6 +815,10 @@
             return Vibration.Status.IGNORED_FOR_ALARM;
         }
 
+        if (currentVibration.attrs.getUsage() == VibrationAttributes.USAGE_RINGTONE) {
+            return Vibration.Status.IGNORED_FOR_RINGTONE;
+        }
+
         if (currentVibration.isRepeating()) {
             return Vibration.Status.IGNORED_FOR_ONGOING;
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 003268b..602e416 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1935,6 +1935,14 @@
         } else if (mSourceRecord != null) {
             return mSourceRecord.getTask();
         } else if (mInTask != null) {
+            // The task is specified from AppTaskImpl, so it may not be attached yet.
+            if (!mInTask.isAttached()) {
+                // Clear reuse task so it can find a proper parent to add the task.
+                if (mReuseTask == mInTask) {
+                    mReuseTask = null;
+                }
+                return getOrCreateRootTask(mStartActivity, mLaunchFlags, mInTask, mOptions);
+            }
             return mInTask;
         } else {
             final Task rootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, null /* task */,
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 22a2c41..6e46fa6 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -26,6 +26,8 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
 import android.os.UserHandle;
 
 /**
@@ -54,6 +56,16 @@
     }
 
     @Override
+    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+            throws RemoteException {
+        try {
+            return super.onTransact(code, data, reply, flags);
+        } catch (RuntimeException e) {
+            throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e);
+        }
+    }
+
+    @Override
     public void finishAndRemoveTask() {
         checkCaller();
 
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index ef0b737..66c625e 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -47,12 +47,6 @@
 class BackNavigationController {
 
     private static final String TAG = "BackNavigationController";
-    // By default, enable new back dispatching without any animations.
-    private static final int BACK_PREDICTABILITY_PROP =
-            SystemProperties.getInt("persist.debug.back_predictability", 1);
-    private static final int ANIMATIONS_MASK = 1 << 1;
-    private static final int SCREENSHOT_MASK = 1 << 2;
-
     @Nullable
     private TaskSnapshotController mTaskSnapshotController;
 
@@ -60,15 +54,15 @@
      * Returns true if the back predictability feature is enabled
      */
     static boolean isEnabled() {
-        return BACK_PREDICTABILITY_PROP > 0;
+        return SystemProperties.getInt("persist.wm.debug.predictive_back", 1) != 0;
     }
 
     static boolean isScreenshotEnabled() {
-        return (BACK_PREDICTABILITY_PROP & SCREENSHOT_MASK) != 0;
+        return SystemProperties.getInt("persist.wm.debug.predictive_back_screenshot", 0) != 0;
     }
 
     private static boolean isAnimationEnabled() {
-        return (BACK_PREDICTABILITY_PROP & ANIMATIONS_MASK) != 0;
+        return SystemProperties.getInt("persist.wm.debug.predictive_back_anim", 0) != 0;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 0038c71..c162e8e 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -175,7 +175,7 @@
             final Rect spaceToFill = transformedBounds != null
                     ? transformedBounds
                     : mActivityRecord.inMultiWindowMode()
-                            ? mActivityRecord.getRootTask().getBounds()
+                            ? mActivityRecord.getTask().getBounds()
                             : mActivityRecord.getRootTask().getParent().getBounds();
             mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
         } else if (mLetterbox != null) {
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 65dca86..83be73a 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -176,13 +176,24 @@
                 t.addTransactionCommittedListener(Runnable::run, () -> {
                     final WindowAnimationSpec animationSpec = a.asWindowAnimationSpec();
 
+                    final Transaction edgeExtensionCreationTransaction = new Transaction();
                     edgeExtendWindow(animationLeash,
                             animationSpec.getRootTaskBounds(), animationSpec.getAnimation(),
-                            mFrameTransaction);
+                            edgeExtensionCreationTransaction);
 
                     synchronized (mLock) {
                         // only run if animation is not yet canceled by this point
                         if (mPreProcessingAnimations.get(animationLeash) == runningAnim) {
+                            // In the case the animation is cancelled, edge extensions are removed
+                            // onAnimationLeashLost which is called before onAnimationCancelled.
+                            // So we need to check if the edge extensions have already been removed
+                            // or not, and if so we don't want to apply the transaction.
+                            synchronized (mEdgeExtensionLock) {
+                                if (!mEdgeExtensions.isEmpty()) {
+                                    edgeExtensionCreationTransaction.apply();
+                                }
+                            }
+
                             mPreProcessingAnimations.remove(animationLeash);
                             mPendingAnimations.put(animationLeash, runningAnim);
                             if (!mAnimationStartDeferred) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 51d68bc..9e54d19 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2606,11 +2606,17 @@
                     return;
                 }
 
+                // Remove immediately if there is display transition because the animation is
+                // usually unnoticeable (e.g. covered by rotation animation) and the animation
+                // bounds could be inconsistent, such as depending on when the window applies
+                // its draw transaction with new rotation.
+                final boolean allowExitAnimation = !getDisplayContent().inTransition();
+
                 if (wasVisible) {
                     final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
 
                     // Try starting an animation.
-                    if (mWinAnimator.applyAnimationLocked(transit, false)) {
+                    if (allowExitAnimation && mWinAnimator.applyAnimationLocked(transit, false)) {
                         ProtoLog.v(WM_DEBUG_ANIM,
                                 "Set animatingExit: reason=remove/applyAnimation win=%s", this);
                         mAnimatingExit = true;
@@ -2624,7 +2630,8 @@
                         mWmService.mAccessibilityController.onWindowTransition(this, transit);
                     }
                 }
-                final boolean isAnimating = mAnimatingExit || isExitAnimationRunningSelfOrParent();
+                final boolean isAnimating = allowExitAnimation
+                        && (mAnimatingExit || isExitAnimationRunningSelfOrParent());
                 final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null
                         && mActivityRecord.isLastWindow(this);
                 // We delay the removal of a window if it has a showing surface that can be used to run
@@ -5284,12 +5291,6 @@
         if (mControllableInsetProvider != null) {
             return;
         }
-        if (getDisplayContent().inTransition()) {
-            // Skip because the animation is usually unnoticeable (e.g. covered by rotation
-            // animation) and the animation bounds could be inconsistent, such as depending
-            // on when the window applies its draw transaction with new rotation.
-            return;
-        }
 
         final DisplayInfo displayInfo = getDisplayInfo();
         anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index e6fd916..d305fc5 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -618,10 +618,17 @@
             IntentFilter intentFilter = new IntentFilter();
             intentFilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED);
             intentFilter.addAction(SmsApplication.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
-            BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId);
-            mBroadcastReceivers.put(userId, broadcastReceiver);
-            mContext.registerReceiverAsUser(
-                    broadcastReceiver, UserHandle.of(userId), intentFilter, null, null);
+
+            if (mBroadcastReceivers.get(userId) == null) {
+                BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId);
+                mBroadcastReceivers.put(userId, broadcastReceiver);
+                mContext.registerReceiverAsUser(
+                        broadcastReceiver, UserHandle.of(userId), intentFilter, null, null);
+            } else {
+                // Stopped was not called on this user before setup is called again. This
+                // could happen during consecutive rapid user switching.
+                if (DEBUG) Log.d(TAG, "PerUserBroadcastReceiver was registered for: " + userId);
+            }
 
             ContentObserver contactsContentObserver = new ContactsContentObserver(
                     BackgroundThread.getHandler());
@@ -639,9 +646,15 @@
                 // Should never occur for local calls.
             }
 
-            PackageMonitor packageMonitor = new PerUserPackageMonitor();
-            packageMonitor.register(mContext, null, UserHandle.of(userId), true);
-            mPackageMonitors.put(userId, packageMonitor);
+            if (mPackageMonitors.get(userId) == null) {
+                PackageMonitor packageMonitor = new PerUserPackageMonitor();
+                packageMonitor.register(mContext, null, UserHandle.of(userId), true);
+                mPackageMonitors.put(userId, packageMonitor);
+            } else {
+                // Stopped was not called on this user before setup is called again. This
+                // could happen during consecutive rapid user switching.
+                if (DEBUG) Log.d(TAG, "PerUserPackageMonitor was registered for: " + userId);
+            }
 
             if (userId == UserHandle.USER_SYSTEM) {
                 // The call log and MMS/SMS messages are shared across user profiles. So only need
diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java b/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java
new file mode 100644
index 0000000..049c745
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.restore;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.backup.BackupAgent;
+import android.platform.test.annotations.Presubmit;
+import android.system.OsConstants;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.backup.FileMetadata;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class FullRestoreEngineTest {
+    private static final String DEFAULT_PACKAGE_NAME = "package";
+    private static final String DEFAULT_DOMAIN_NAME = "domain";
+    private static final String NEW_PACKAGE_NAME = "new_package";
+    private static final String NEW_DOMAIN_NAME = "new_domain";
+
+    private FullRestoreEngine mRestoreEngine;
+
+    @Before
+    public void setUp() {
+        mRestoreEngine = new FullRestoreEngine();
+    }
+
+    @Test
+    public void shouldSkipReadOnlyDir_skipsAllReadonlyDirsAndTheirChildren() {
+        // Create the file tree.
+        TestFile[] testFiles = new TestFile[] {
+                TestFile.dir("root"),
+                TestFile.file("root/auth_token"),
+                TestFile.dir("root/media"),
+                TestFile.file("root/media/picture1.png"),
+                TestFile.file("root/push_token.txt"),
+                TestFile.dir("root/read-only-dir-1").markReadOnly().expectSkipped(),
+                TestFile.dir("root/read-only-dir-1/writable-subdir").expectSkipped(),
+                TestFile.file("root/read-only-dir-1/writable-subdir/writable-file").expectSkipped(),
+                TestFile.dir("root/read-only-dir-1/writable-subdir/read-only-subdir-2")
+                        .markReadOnly().expectSkipped(),
+                TestFile.file("root/read-only-dir-1/writable-file").expectSkipped(),
+                TestFile.file("root/random-stuff.txt"),
+                TestFile.dir("root/database"),
+                TestFile.file("root/database/users.db"),
+                TestFile.dir("root/read-only-dir-2").markReadOnly().expectSkipped(),
+                TestFile.file("root/read-only-dir-2/writable-file-1").expectSkipped(),
+                TestFile.file("root/read-only-dir-2/writable-file-2").expectSkipped(),
+        };
+
+        assertCorrectItemsAreSkipped(testFiles);
+    }
+
+    @Test
+    public void shouldSkipReadOnlyDir_onlySkipsChildrenUnderTheSamePackage() {
+        TestFile[] testFiles = new TestFile[]{
+                TestFile.dir("read-only-dir").markReadOnly().expectSkipped(),
+                TestFile.file("read-only-dir/file").expectSkipped(),
+                TestFile.file("read-only-dir/file-from-different-package")
+                        .setPackage(NEW_PACKAGE_NAME),
+        };
+
+        assertCorrectItemsAreSkipped(testFiles);
+    }
+
+    @Test
+    public void shouldSkipReadOnlyDir_onlySkipsChildrenUnderTheSameDomain() {
+        TestFile[] testFiles = new TestFile[]{
+                TestFile.dir("read-only-dir").markReadOnly().expectSkipped(),
+                TestFile.file("read-only-dir/file").expectSkipped(),
+                TestFile.file("read-only-dir/file-from-different-domain")
+                        .setDomain(NEW_DOMAIN_NAME),
+        };
+
+        assertCorrectItemsAreSkipped(testFiles);
+    }
+
+    private void assertCorrectItemsAreSkipped(TestFile[] testFiles) {
+        // Verify all directories marked with .expectSkipped are skipped.
+        for (TestFile testFile : testFiles) {
+            boolean actualExcluded = mRestoreEngine.shouldSkipReadOnlyDir(testFile.mMetadata);
+            boolean expectedExcluded = testFile.mShouldSkip;
+            assertWithMessage(testFile.mMetadata.path).that(actualExcluded).isEqualTo(
+                    expectedExcluded);
+        }
+    }
+
+    private static class TestFile {
+        private final FileMetadata mMetadata;
+        private boolean mShouldSkip;
+
+        static TestFile dir(String path) {
+            return new TestFile(path, BackupAgent.TYPE_DIRECTORY);
+        }
+
+        static TestFile file(String path) {
+            return new TestFile(path, BackupAgent.TYPE_FILE);
+        }
+
+        TestFile markReadOnly() {
+            mMetadata.mode = 0;
+            return this;
+        }
+
+        TestFile expectSkipped() {
+            mShouldSkip = true;
+            return this;
+        }
+
+        TestFile setPackage(String packageName) {
+            mMetadata.packageName = packageName;
+            return this;
+        }
+
+        TestFile setDomain(String domain) {
+            mMetadata.domain = domain;
+            return this;
+        }
+
+        private TestFile(String path, int type) {
+            FileMetadata metadata = new FileMetadata();
+            metadata.path = path;
+            metadata.type = type;
+            metadata.packageName = DEFAULT_PACKAGE_NAME;
+            metadata.domain = DEFAULT_DOMAIN_NAME;
+            metadata.mode = OsConstants.S_IWUSR; // Mark as writable.
+            mMetadata = metadata;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
index 1d9ea4b..0b144dc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
@@ -41,6 +41,8 @@
 @RunWith(JUnit4.class)
 public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTest {
 
+    private static final String SDK_INT_PLUS_ONE = "" + (Build.VERSION.SDK_INT + 1);
+    private static final String SDK_INT_PLUS_TWO = "" + (Build.VERSION.SDK_INT + 2);
     private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries =
             new ArrayMap<>(8);
 
@@ -51,14 +53,19 @@
 
     private void installSharedLibraries() throws Exception {
         mSharedLibraries.clear();
-        insertLibrary("foo", 0, 0);
-        insertLibrary("fooBcpSince30", 30, 0);
-        insertLibrary("fooBcpBefore30", 0, 30);
-        insertLibrary("fooFromFuture", Build.VERSION.SDK_INT + 2, 0);
+        insertLibrary("foo", null, null);
+        insertLibrary("fooBcpSince30", "30", null);
+        insertLibrary("fooBcpBefore30", null, "30");
+        // simulate libraries being added to the BCP in a future release
+        insertLibrary("fooSinceFuture", SDK_INT_PLUS_ONE, null);
+        insertLibrary("fooSinceFutureCodename", "Z", null);
+        // simulate libraries being removed from the BCP in a future release
+        insertLibrary("fooBcpBeforeFuture", null, SDK_INT_PLUS_ONE);
+        insertLibrary("fooBcpBeforeFutureCodename", null, "Z");
     }
 
-    private void insertLibrary(String libraryName, int onBootclasspathSince,
-            int onBootclasspathBefore) {
+    private void insertLibrary(String libraryName, String onBootclasspathSince,
+            String onBootclasspathBefore) {
         mSharedLibraries.put(libraryName, new SystemConfig.SharedLibraryEntry(
                 libraryName,
                 "foo.jar",
@@ -112,7 +119,7 @@
     }
 
     @Test
-    public void testBcpSince11kNotAppliedWithoutLibrary() {
+    public void testBcpSinceFutureNotAppliedWithoutLibrary() {
         ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                 .setTargetSdkVersion(Build.VERSION_CODES.R)
                 .hideAsParsed());
@@ -128,15 +135,17 @@
     }
 
     @Test
-    public void testBcpSince11kNotAppliedWithLibrary() {
+    public void testBcpSinceFutureNotAppliedWithLibrary() {
         ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                 .setTargetSdkVersion(Build.VERSION_CODES.R)
-                .addUsesLibrary("fooFromFuture")
+                .addUsesLibrary("fooSinceFuture")
+                .addUsesLibrary("fooSinceFutureCodename")
                 .hideAsParsed());
 
         AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                 .setTargetSdkVersion(Build.VERSION_CODES.R)
-                .addUsesLibrary("fooFromFuture")
+                .addUsesLibrary("fooSinceFuture")
+                .addUsesLibrary("fooSinceFutureCodename")
                 .hideAsParsed())
                 .hideAsFinal();
 
@@ -183,7 +192,7 @@
      */
     @Test
     public void testBcpRemovedThenAddedPast() {
-        insertLibrary("fooBcpRemovedThenAdded", 30, 28);
+        insertLibrary("fooBcpRemovedThenAdded", "30", "28");
 
         ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                 .setTargetSdkVersion(Build.VERSION_CODES.N)
@@ -207,7 +216,8 @@
      */
     @Test
     public void testBcpRemovedThenAddedMiddle_targetQ() {
-        insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);
+        insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_ONE, "30");
+        insertLibrary("fooBcpRemovedThenAddedCodename", "Z", "30");
 
         ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                 .setTargetSdkVersion(Build.VERSION_CODES.Q)
@@ -217,6 +227,7 @@
                 .setTargetSdkVersion(Build.VERSION_CODES.Q)
                 .addUsesLibrary("fooBcpRemovedThenAdded")
                 .addUsesLibrary("fooBcpBefore30")
+                .addUsesLibrary("fooBcpRemovedThenAddedCodename")
                 .hideAsParsed())
                 .hideAsFinal();
 
@@ -232,7 +243,8 @@
      */
     @Test
     public void testBcpRemovedThenAddedMiddle_targetR() {
-        insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);
+        insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_ONE, "30");
+        insertLibrary("fooBcpRemovedThenAddedCodename", "Z", "30");
 
         ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                 .setTargetSdkVersion(Build.VERSION_CODES.R)
@@ -256,7 +268,8 @@
      */
     @Test
     public void testBcpRemovedThenAddedMiddle_targetR_usingLib() {
-        insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);
+        insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_ONE, "30");
+        insertLibrary("fooBcpRemovedThenAddedCodename", "Z", "30");
 
         ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                 .setTargetSdkVersion(Build.VERSION_CODES.R)
@@ -274,6 +287,82 @@
         checkBackwardsCompatibility(before, after);
     }
 
+    /**
+     * Test a library that was first removed from the BCP [to a mainline module] and later was
+     * moved back to the BCP via a mainline module update. Both things happening in future SDKs.
+     */
+    @Test
+    public void testBcpRemovedThenAddedFuture() {
+        insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_TWO, SDK_INT_PLUS_ONE);
+        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.R)
+                .hideAsParsed());
+
+        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.R)
+                .hideAsParsed())
+                .hideAsFinal();
+
+        // in this example, we are at the point where the library is still in the BCP
+        checkBackwardsCompatibility(before, after);
+    }
+
+    /**
+     * Test a library that was first removed from the BCP [to a mainline module] and later was
+     * moved back to the BCP via a mainline module update. Both things happening in future SDKs.
+     */
+    @Test
+    public void testBcpRemovedThenAddedFuture_usingLib() {
+        insertLibrary("fooBcpRemovedThenAdded", SDK_INT_PLUS_TWO, SDK_INT_PLUS_ONE);
+
+        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE))
+                .addUsesLibrary("fooBcpRemovedThenAdded")
+                .hideAsParsed());
+
+        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE))
+                .hideAsParsed())
+                .hideAsFinal();
+
+        // in this example, we are at the point where the library was removed from the BCP
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void testBcpBeforeFuture() {
+        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.R)
+                .addUsesLibrary("fooBcpBeforeFuture")
+                .addUsesLibrary("fooBcpBeforeFutureCodename")
+                .hideAsParsed());
+
+        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.R)
+                .hideAsParsed())
+                .hideAsFinal();
+
+        // in this example, we are at the point where the library was removed from the BCP
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void testBcpBeforeFuture_futureTargetSdk() {
+        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE))
+                .addUsesLibrary("fooBcpBeforeFuture")
+                .addUsesLibrary("fooBcpBeforeFutureCodename")
+                .hideAsParsed());
+
+        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Integer.parseInt(SDK_INT_PLUS_ONE))
+                .hideAsParsed())
+                .hideAsFinal();
+
+        // in this example, we are at the point where the library was removed from the BCP
+        checkBackwardsCompatibility(before, after);
+    }
+
     private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
         checkBackwardsCompatibility(before, after,
                 () -> new ApexSharedLibraryUpdater(mSharedLibraries));
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index bfdffc0..20486b3 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -429,18 +429,40 @@
     public void readPermissions_allowLibs_parsesSimpleLibrary() throws IOException {
         String contents =
                 "<permissions>\n"
-                + "    <library \n"
-                + "        name=\"foo\"\n"
-                + "        file=\"" + mFooJar + "\"\n"
-                + "        on-bootclasspath-before=\"10\"\n"
-                + "        on-bootclasspath-since=\"20\"\n"
-                + "     />\n\n"
-                + " </permissions>";
+                        + "    <library \n"
+                        + "        name=\"foo\"\n"
+                        + "        file=\"" + mFooJar + "\"\n"
+                        + "        on-bootclasspath-before=\"10\"\n"
+                        + "        on-bootclasspath-since=\"20\"\n"
+                        + "     />\n\n"
+                        + " </permissions>";
         parseSharedLibraries(contents);
         assertFooIsOnlySharedLibrary();
         SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
-        assertThat(entry.onBootclasspathBefore).isEqualTo(10);
-        assertThat(entry.onBootclasspathSince).isEqualTo(20);
+        assertThat(entry.onBootclasspathBefore).isEqualTo("10");
+        assertThat(entry.onBootclasspathSince).isEqualTo("20");
+    }
+
+    /**
+     * Tests that readPermissions works correctly for a library with on-bootclasspath-before
+     * and on-bootclasspath-since that uses codenames.
+     */
+    @Test
+    public void readPermissions_allowLibs_parsesSimpleLibraryWithCodenames() throws IOException {
+        String contents =
+                "<permissions>\n"
+                        + "    <library \n"
+                        + "        name=\"foo\"\n"
+                        + "        file=\"" + mFooJar + "\"\n"
+                        + "        on-bootclasspath-before=\"Q\"\n"
+                        + "        on-bootclasspath-since=\"W\"\n"
+                        + "     />\n\n"
+                        + " </permissions>";
+        parseSharedLibraries(contents);
+        assertFooIsOnlySharedLibrary();
+        SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
+        assertThat(entry.onBootclasspathBefore).isEqualTo("Q");
+        assertThat(entry.onBootclasspathSince).isEqualTo("W");
     }
 
     /**
@@ -461,8 +483,8 @@
         parseSharedLibraries(contents);
         assertFooIsOnlySharedLibrary();
         SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
-        assertThat(entry.onBootclasspathBefore).isEqualTo(10);
-        assertThat(entry.onBootclasspathSince).isEqualTo(20);
+        assertThat(entry.onBootclasspathBefore).isEqualTo("10");
+        assertThat(entry.onBootclasspathSince).isEqualTo("20");
     }
 
     /**
@@ -543,12 +565,20 @@
      */
     @Test
     public void readPermissions_allowLibs_allowsCurrentMaxSdk() throws IOException {
+        // depending on whether this test is running before or after finalization, we need to
+        // pass a different parameter
+        String parameter;
+        if ("REL".equals(Build.VERSION.CODENAME)) {
+            parameter = "" + Build.VERSION.SDK_INT;
+        } else {
+            parameter = "ZZZ";
+        }
         String contents =
                 "<permissions>\n"
                 + "    <library \n"
                 + "        name=\"foo\"\n"
                 + "        file=\"" + mFooJar + "\"\n"
-                + "        max-device-sdk=\"" + Build.VERSION.SDK_INT + "\"\n"
+                + "        max-device-sdk=\"" + parameter + "\"\n"
                 + "     />\n\n"
                 + " </permissions>";
         parseSharedLibraries(contents);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index f3d494d..4fbf006 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -602,10 +602,15 @@
                 VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK);
         VibratorManagerService service = createSystemReadyService();
         mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+
+        // The haptic feedback should be ignored in low power, but not the ringtone. The end
+        // of the test asserts which actual effects ended up playing.
         vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS);
         vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
         assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
                 service, TEST_TIMEOUT_MILLIS));
+        // Allow the ringtone to complete, as the other vibrations won't cancel it.
+        assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
 
         mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
         vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK),
@@ -815,6 +820,29 @@
     }
 
     @Test
+    public void vibrate_withOngoingRingtoneVibration_ignoresEffect() throws Exception {
+        mockVibrators(1);
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+        VibratorManagerService service = createSystemReadyService();
+
+        VibrationEffect alarmEffect = VibrationEffect.createWaveform(
+                new long[]{10_000, 10_000}, new int[]{128, 255}, -1);
+        vibrate(service, alarmEffect, new VibrationAttributes.Builder().setUsage(
+                VibrationAttributes.USAGE_RINGTONE).build());
+
+        // VibrationThread will start this vibration async, so wait before checking it started.
+        assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
+                service, TEST_TIMEOUT_MILLIS));
+
+        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
+                HAPTIC_FEEDBACK_ATTRS);
+
+        // Wait before checking it never played a second effect.
+        assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
+                service, /* timeout= */ 50));
+    }
+
+    @Test
     public void vibrate_withInputDevices_vibratesInputDevices() throws Exception {
         mockVibrators(1);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 9902e83..908de34 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1139,18 +1139,8 @@
                 true /* createdByOrganizer */);
         sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
 
-        starter.startActivityInner(
-                /* r */targetRecord,
-                /* sourceRecord */ sourceRecord,
-                /* voiceSession */null,
-                /* voiceInteractor */ null,
-                /* startFlags */ 0,
-                /* doResume */true,
-                /* options */null,
-                /* inTask */null,
-                /* inTaskFragment */ taskFragment,
-                /* restrictedBgActivity */false,
-                /* intentGrants */null);
+        startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+                null /* inTask */, taskFragment);
 
         assertFalse(taskFragment.hasChild());
     }
@@ -1167,18 +1157,8 @@
         taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class), SYSTEM_UID,
                 "system_uid");
 
-        starter.startActivityInner(
-                /* r */targetRecord,
-                /* sourceRecord */ sourceRecord,
-                /* voiceSession */null,
-                /* voiceInteractor */ null,
-                /* startFlags */ 0,
-                /* doResume */true,
-                /* options */null,
-                /* inTask */null,
-                /* inTaskFragment */ taskFragment,
-                /* restrictedBgActivity */false,
-                /* intentGrants */null);
+        startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+                null /* inTask */, taskFragment);
 
         assertTrue(taskFragment.hasChild());
     }
@@ -1195,18 +1175,8 @@
         taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class),
                 targetRecord.getUid(), "test_process_name");
 
-        starter.startActivityInner(
-                /* r */targetRecord,
-                /* sourceRecord */ sourceRecord,
-                /* voiceSession */null,
-                /* voiceInteractor */ null,
-                /* startFlags */ 0,
-                /* doResume */true,
-                /* options */null,
-                /* inTask */null,
-                /* inTaskFragment */ taskFragment,
-                /* restrictedBgActivity */false,
-                /* intentGrants */null);
+        startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+                null /* inTask */, taskFragment);
 
         assertTrue(taskFragment.hasChild());
     }
@@ -1231,18 +1201,8 @@
         doReturn(true).when(signingDetails).hasAncestorOrSelfWithDigest(any());
         doReturn(signingDetails).when(androidPackage).getSigningDetails();
 
-        starter.startActivityInner(
-                /* r */targetRecord,
-                /* sourceRecord */ sourceRecord,
-                /* voiceSession */null,
-                /* voiceInteractor */ null,
-                /* startFlags */ 0,
-                /* doResume */true,
-                /* options */null,
-                /* inTask */null,
-                /* inTaskFragment */ taskFragment,
-                /* restrictedBgActivity */false,
-                /* intentGrants */null);
+        startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+                null /* inTask */, taskFragment);
 
         assertTrue(taskFragment.hasChild());
     }
@@ -1258,23 +1218,30 @@
 
         targetRecord.info.flags |= ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
 
-        starter.startActivityInner(
-                /* r */targetRecord,
-                /* sourceRecord */ sourceRecord,
-                /* voiceSession */null,
-                /* voiceInteractor */ null,
-                /* startFlags */ 0,
-                /* doResume */true,
-                /* options */null,
-                /* inTask */null,
-                /* inTaskFragment */ taskFragment,
-                /* restrictedBgActivity */false,
-                /* intentGrants */null);
+        startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
+                null /* inTask */, taskFragment);
 
         assertTrue(taskFragment.hasChild());
     }
 
     @Test
+    public void testStartActivityInner_inTask() {
+        final ActivityStarter starter = prepareStarter(0, false);
+        // Simulate an app uses AppTask to create a non-attached task, and then it requests to
+        // start activity in the task.
+        final Task inTask = new TaskBuilder(mSupervisor).setTaskDisplayArea(null).setTaskId(123)
+                .build();
+        inTask.inRecents = true;
+        assertFalse(inTask.isAttached());
+        final ActivityRecord target = new ActivityBuilder(mAtm).build();
+        startActivityInner(starter, target, null /* source */, null /* options */, inTask,
+                null /* inTaskFragment */);
+
+        assertTrue(inTask.isAttached());
+        assertEquals(inTask, target.getTask());
+    }
+
+    @Test
     public void testLaunchCookie_newAndExistingTask() {
         final ActivityStarter starter = prepareStarter(0, false);
 
@@ -1322,21 +1289,20 @@
 
         // Start the target launch-into-pip activity from a source
         final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        starter.startActivityInner(
-                /* r */ targetRecord,
-                /* sourceRecord */ sourceRecord,
-                /* voiceSession */ null,
-                /* voiceInteractor */ null,
-                /* startFlags */ 0,
-                /* doResume */ true,
-                /* options */ opts,
-                /* inTask */ null,
-                /* inTaskFragment */ null,
-                /* restrictedBgActivity */ false,
-                /* intentGrants */ null);
+        startActivityInner(starter, targetRecord, sourceRecord, opts,
+                null /* inTask */, null /* inTaskFragment */);
 
         // Verify the ActivityRecord#getLaunchIntoPipHostActivity points to sourceRecord.
         assertThat(targetRecord.getLaunchIntoPipHostActivity()).isNotNull();
         assertEquals(targetRecord.getLaunchIntoPipHostActivity(), sourceRecord);
     }
+
+    private static void startActivityInner(ActivityStarter starter, ActivityRecord target,
+            ActivityRecord source, ActivityOptions options, Task inTask,
+            TaskFragment inTaskFragment) {
+        starter.startActivityInner(target, source, null /* voiceSession */,
+                null /* voiceInteractor */, 0 /* startFlags */, true /* doResume */,
+                options, inTask, inTaskFragment, false /* restrictedBgActivity */,
+                null /* intentGrants */);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
similarity index 74%
rename from services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
index f968999..a8282600 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.START_ABORTED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
@@ -26,6 +27,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
 
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
@@ -45,13 +47,13 @@
 import java.util.Set;
 
 /**
- * Tests for the {@link DisplayWindowPolicyControllerHelper} class.
+ * Tests for the {@link DisplayWindowPolicyController} class.
  *
  * Build/Install/Run:
- *  atest WmTests:DisplayWindowPolicyControllerHelperTests
+ *  atest WmTests:DisplayWindowPolicyControllerTests
  */
 @RunWith(WindowTestRunner.class)
-public class DisplayWindowPolicyControllerHelperTests extends WindowTestsBase {
+public class DisplayWindowPolicyControllerTests extends WindowTestsBase {
     private static final int TEST_USER_0_ID = 0;
     private static final int TEST_USER_1_ID = 10;
 
@@ -152,8 +154,51 @@
         assertTrue(mSecondaryDisplay.mDwpcHelper.isWindowingModeSupported(WINDOWING_MODE_PINNED));
     }
 
+    @Test
+    public void testInterestedWindowFlags() {
+        final int fakeFlag1 = 0x00000010;
+        final int fakeFlag2 = 0x00000100;
+        final int fakeSystemFlag1 = 0x00000010;
+        final int fakeSystemFlag2 = 0x00000100;
+
+        mDwpc.setInterestedWindowFlags(fakeFlag1, fakeSystemFlag1);
+
+        assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag1, fakeSystemFlag1));
+        assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag1, fakeSystemFlag2));
+        assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag2, fakeSystemFlag1));
+        assertFalse(mDwpc.isInterestedWindowFlags(fakeFlag2, fakeSystemFlag2));
+    }
+
+    @Test
+    public void testCanContainActivities() {
+        ActivityStarter starter = new ActivityStarter(mock(ActivityStartController.class), mAtm,
+                mSupervisor, mock(ActivityStartInterceptor.class));
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(mSecondaryDisplay).build();
+        final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setTask(task).build();
+        final ActivityRecord disallowedRecord =
+                new ActivityBuilder(mAtm).setComponent(mDwpc.DISALLOWED_ACTIVITY).build();
+
+        int result = starter.startActivityInner(
+                disallowedRecord,
+                sourceRecord,
+                /* voiceSession */null,
+                /* voiceInteractor */ null,
+                /* startFlags */ 0,
+                /* doResume */true,
+                /* options */null,
+                /* inTask */null,
+                /* inTaskFragment */ null,
+                /* restrictedBgActivity */false,
+                /* intentGrants */null);
+
+        assertEquals(result, START_ABORTED);
+    }
+
     private class TestDisplayWindowPolicyController extends DisplayWindowPolicyController {
 
+        public ComponentName DISALLOWED_ACTIVITY =
+                new ComponentName("fake.package", "DisallowedActivity");
+
         ComponentName mTopActivity = null;
         int mTopActivityUid = UserHandle.USER_NULL;
         ArraySet<Integer> mRunningUids = new ArraySet<>();
@@ -161,7 +206,14 @@
         @Override
         public boolean canContainActivities(@NonNull List<ActivityInfo> activities,
                 @WindowConfiguration.WindowingMode int windowingMode) {
-            return false;
+            final int activityCount = activities.size();
+            for (int i = 0; i < activityCount; i++) {
+                final ActivityInfo aInfo = activities.get(i);
+                if (aInfo.getComponentName().equals(DISALLOWED_ACTIVITY)) {
+                    return false;
+                }
+            }
+            return true;
         }
 
         @Override
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 8d7fab4..a2266fb 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1959,6 +1959,13 @@
             "nr_advanced_threshold_bandwidth_khz_int";
 
     /**
+     * Boolean indicating if operator name should be shown in the status bar
+     * @hide
+     */
+    public static final String KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL =
+            "show_operator_name_in_statusbar_bool";
+
+    /**
      * The string is used to filter redundant string from PLMN Network Name that's supplied by
      * specific carrier.
      *
@@ -8913,6 +8920,7 @@
         sDefaults.putStringArray(KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_WORLD_MODE_ENABLED_BOOL, false);
         sDefaults.putString(KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING, "");
+        sDefaults.putBoolean(KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
         sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL, false);
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index 3843a62..249f740 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -21,6 +21,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.telephony.Rlog;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -227,7 +230,6 @@
         this.mIccIdAccessRestricted = iccIdAccessRestricted;
     }
 
-
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
@@ -261,7 +263,7 @@
                 + ", mCardId="
                 + mCardId
                 + ", mEid="
-                + mEid
+                + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid)
                 + ", mPhysicalSlotIndex="
                 + mPhysicalSlotIndex
                 + ", mIsRemovable="
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 17ce450..dd3639a 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -279,7 +279,7 @@
                 + ", mIsEuicc="
                 + mIsEuicc
                 + ", mCardId="
-                + mCardId
+                + SubscriptionInfo.givePrintableIccid(mCardId)
                 + ", cardState="
                 + mCardStateInfo
                 + ", mIsExtendedApduSupported="
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
new file mode 100644
index 0000000..172c433
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+
+class ImeEditorPopupDialogAppHelper @JvmOverloads constructor(
+    instr: Instrumentation,
+    private val rotation: Int,
+    private val imePackageName: String = IME_PACKAGE,
+    launcherName: String = ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME,
+    component: FlickerComponentName =
+            ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
+) : ImeAppHelper(instr, launcherName, component) {
+    override fun openIME(
+        device: UiDevice,
+        wmHelper: WindowManagerStateHelper?
+    ) {
+        val editText = device.wait(Until.findObject(By.text("focused editText")), FIND_TIMEOUT)
+
+        require(editText != null) {
+            "Text field not found, this usually happens when the device " +
+                    "was left in an unknown state (e.g. in split screen)"
+        }
+        editText.click()
+        waitIMEShown(device, wmHelper)
+    }
+
+    fun dismissDialog(wmHelper: WindowManagerStateHelper) {
+        val dismissButton = uiDevice.wait(
+                Until.findObject(By.text("Dismiss")), FIND_TIMEOUT)
+
+        // Pressing back key to dismiss the dialog
+        if (dismissButton != null) {
+            dismissButton.click()
+            wmHelper.waitForAppTransitionIdle()
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
new file mode 100644
index 0000000..bff099e
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.ime
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group4
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.ImeEditorPopupDialogAppHelper
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.flicker.traces.region.RegionSubject
+import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group4
+class CloseImeEditorPopupDialogTest(private val testSpec: FlickerTestParameter) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val imeTestApp = ImeEditorPopupDialogAppHelper(instrumentation, testSpec.startRotation)
+
+    @FlickerBuilderProvider
+    fun buildFlicker(): FlickerBuilder {
+        return FlickerBuilder(instrumentation).apply {
+            setup {
+                eachRun {
+                    imeTestApp.launchViaIntent(wmHelper)
+                    imeTestApp.openIME(device, wmHelper)
+                }
+            }
+            transitions {
+                imeTestApp.dismissDialog(wmHelper)
+                instrumentation.uiAutomation.syncInputTransactions()
+            }
+            teardown {
+                eachRun {
+                    device.pressHome()
+                    wmHelper.waitForHomeActivityVisible()
+                    imeTestApp.exit()
+                }
+            }
+        }
+    }
+
+    @Postsubmit
+    @Test
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
+
+    @Postsubmit
+    @Test
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+    @Postsubmit
+    @Test
+    fun imeWindowBecameInvisible() = testSpec.imeWindowBecomesInvisible()
+
+    @Postsubmit
+    @Test
+    fun imeLayerAndImeSnapshotVisibleOnScreen() {
+        testSpec.assertLayers {
+            this.isVisible(FlickerComponentName.IME)
+                    .then()
+                    .isVisible(FlickerComponentName.IME_SNAPSHOT)
+                    .then()
+                    .isInvisible(FlickerComponentName.IME)
+        }
+    }
+
+    @Postsubmit
+    @Test
+    fun imeSnapshotAssociatedOnAppVisibleRegion() {
+        testSpec.assertLayers {
+            this.invoke("imeSnapshotAssociatedOnAppVisibleRegion") {
+                val imeSnapshotLayers = it.subjects.filter {
+                    subject -> subject.name.contains(
+                        FlickerComponentName.IME_SNAPSHOT.toLayerName()) && subject.isVisible
+                }
+                if (imeSnapshotLayers.isNotEmpty()) {
+                    val visibleAreas = imeSnapshotLayers.mapNotNull { imeSnapshotLayer ->
+                        imeSnapshotLayer.layer?.visibleRegion }.toTypedArray()
+                    val imeVisibleRegion = RegionSubject.assertThat(visibleAreas, this, timestamp)
+                    val appVisibleRegion = it.visibleRegion(imeTestApp.component)
+                    if (imeVisibleRegion.region.isNotEmpty) {
+                        imeVisibleRegion.coversAtMost(appVisibleRegion.region)
+                    }
+                }
+            }
+        }
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<FlickerTestParameter> {
+            return FlickerTestParameterFactory.getInstance()
+                    .getConfigNonRotationTests(
+                            repetitions = 2,
+                            supportedNavigationModes = listOf(
+                                    WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+                                    WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+                            ),
+                            supportedRotations = listOf(Surface.ROTATION_0)
+                    )
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
index 7f49663..1b60403 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -79,7 +79,7 @@
     /**
      * Checks that [FlickerComponentName.IME] layer is visible at the end of the transition
      */
-    @Presubmit
+    @FlakyTest(bugId = 227142436)
     @Test
     fun imeLayerExistsEnd() {
         testSpec.assertLayersEnd {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
index 9c9dedc2..4b8a8c8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker.quickswitch
 
 import android.platform.test.annotations.RequiresDevice
+import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.annotation.Group1
@@ -44,6 +45,7 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group1
+@FlakyTest(bugId = 228009808)
 open class QuickSwitchBetweenTwoAppsForwardTest_ShellTransit(testSpec: FlickerTestParameter)
     : QuickSwitchBetweenTwoAppsForwardTest(testSpec) {
     @Before
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 739fe02..7f513b2 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -119,5 +119,16 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+        <activity android:name=".ImeEditorPopupDialogActivity"
+            android:taskAffinity="com.android.server.wm.flicker.testapp.ImeEditorPopupDialogActivity"
+            android:configChanges="orientation|screenSize"
+            android:theme="@style/CutoutShortEdges"
+            android:label="ImeEditorPopupDialogActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 3040a09..18c95cf 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -56,6 +56,7 @@
     public static final ComponentName LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME =
             new ComponentName(FLICKER_APP_PACKAGE,
                     FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity");
+
     public static final String DIALOG_THEMED_ACTIVITY = "DialogThemedActivity";
     public static final ComponentName DIALOG_THEMED_ACTIVITY_COMPONENT_NAME =
             new ComponentName(FLICKER_APP_PACKAGE,
@@ -65,4 +66,10 @@
     public static final ComponentName PORTRAIT_ONLY_ACTIVITY_COMPONENT_NAME =
             new ComponentName(FLICKER_APP_PACKAGE,
                     FLICKER_APP_PACKAGE + ".PortraitOnlyActivity");
+
+    public static final String EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME =
+            "ImeEditorPopupDialogActivity";
+    public static final ComponentName EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME =
+            new ComponentName(FLICKER_APP_PACKAGE,
+                    FLICKER_APP_PACKAGE + ".ImeEditorPopupDialogActivity");
 }
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java
new file mode 100644
index 0000000..a8613f5
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+public class ImeEditorPopupDialogActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        WindowManager.LayoutParams p = getWindow().getAttributes();
+        p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+                .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        getWindow().setAttributes(p);
+        LinearLayout layout = new LinearLayout(this);
+        layout.setOrientation(LinearLayout.VERTICAL);
+        setContentView(R.layout.activity_simple);
+
+        final EditText editText = new EditText(this);
+        editText.setHint("focused editText");
+        final AlertDialog dialog = new AlertDialog.Builder(this)
+                .setView(editText)
+                .setPositiveButton("Dismiss", (d, which) -> d.dismiss())
+                .create();
+        dialog.show();
+    }
+}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index bd0a4bc..bfb3285 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -165,6 +165,7 @@
     ],
     proto: {
         export_proto_headers: true,
+        type: "full",
     },
     defaults: ["aapt2_defaults"],
 }