Merge "[Motion][Split shade] Fix left side of split shade sometimes not overscrolling" into tm-qpr-dev
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
index 517e3ce..31c92ba 100644
--- a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
@@ -70,6 +70,9 @@
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             ZipFile zf = new ZipFile(mFile);
+            state.pauseTiming();
+            zf.close();
+            state.resumeTiming();
         }
     }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 9e9e985..802458b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5894,16 +5894,16 @@
 
         final boolean movedToDifferentDisplay = isDifferentDisplay(activity.getDisplayId(),
                 displayId);
-        final Configuration currentConfig = activity.mCurrentConfig;
-        final int diff = currentConfig.diffPublicOnly(newConfig);
-        final boolean hasPublicConfigChange = diff != 0;
+        final Configuration currentResConfig = activity.getResources().getConfiguration();
+        final int diff = currentResConfig.diffPublicOnly(newConfig);
+        final boolean hasPublicResConfigChange = diff != 0;
         final ActivityClientRecord r = getActivityClient(activityToken);
         // TODO(b/173090263): Use diff instead after the improvement of AssetManager and
         // ResourcesImpl constructions.
-        final boolean shouldUpdateResources = hasPublicConfigChange
-                || shouldUpdateResources(activityToken, currentConfig, newConfig, amOverrideConfig,
-                movedToDifferentDisplay, hasPublicConfigChange);
-        final boolean shouldReportChange = shouldReportChange(diff, currentConfig, newConfig,
+        final boolean shouldUpdateResources = hasPublicResConfigChange
+                || shouldUpdateResources(activityToken, currentResConfig, newConfig,
+                amOverrideConfig, movedToDifferentDisplay, hasPublicResConfigChange);
+        final boolean shouldReportChange = shouldReportChange(activity.mCurrentConfig, newConfig,
                 r != null ? r.mSizeConfigurations : null,
                 activity.mActivityInfo.getRealConfigChanged());
         // Nothing significant, don't proceed with updating and reporting.
@@ -5927,9 +5927,6 @@
                 amOverrideConfig, contextThemeWrapperOverrideConfig);
         mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);
 
-        activity.mConfigChangeFlags = 0;
-        activity.mCurrentConfig = new Configuration(newConfig);
-
         // Apply the ContextThemeWrapper override if necessary.
         // NOTE: Make sure the configurations are not modified, as they are treated as immutable
         // in many places.
@@ -5940,8 +5937,10 @@
             activity.dispatchMovedToDisplay(displayId, configToReport);
         }
 
+        activity.mConfigChangeFlags = 0;
         if (shouldReportChange) {
             activity.mCalled = false;
+            activity.mCurrentConfig = new Configuration(newConfig);
             activity.onConfigurationChanged(configToReport);
             if (!activity.mCalled) {
                 throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +
@@ -5956,8 +5955,6 @@
      * Returns {@code true} if {@link Activity#onConfigurationChanged(Configuration)} should be
      * dispatched.
      *
-     * @param publicDiff Usually computed by {@link Configuration#diffPublicOnly(Configuration)}.
-     *                   This parameter is to prevent we compute it again.
      * @param currentConfig The current configuration cached in {@link Activity#mCurrentConfig}.
      *                      It is {@code null} before the first config update from the server side.
      * @param newConfig The updated {@link Configuration}
@@ -5966,9 +5963,10 @@
      * @return {@code true} if the config change should be reported to the Activity
      */
     @VisibleForTesting
-    public static boolean shouldReportChange(int publicDiff, @Nullable Configuration currentConfig,
+    public static boolean shouldReportChange(@Nullable Configuration currentConfig,
             @NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets sizeBuckets,
             int handledConfigChanges) {
+        final int publicDiff = currentConfig.diffPublicOnly(newConfig);
         // Don't report the change if there's no public diff between current and new config.
         if (publicDiff == 0) {
             return false;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b7f1136..0e1a546 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9263,6 +9263,21 @@
     }
 
     /**
+     * Checks if the specified component is the supervision component.
+     * @hide
+     */
+    public boolean isSupervisionComponent(@NonNull ComponentName who) {
+        if (mService != null) {
+            try {
+                return getService().isSupervisionComponent(who);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
      * @hide
      * @return the human readable name of the organisation associated with this DPM or {@code null}
      *         if one is not set.
diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
index 4999245..ca00154 100644
--- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
+++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
@@ -301,6 +301,7 @@
          * Sets a {@link PersistableBundle} that contains admin-specific extras.
          */
         @NonNull
+        //TODO(b/235783053) The adminExtras parameter is actually @Nullable.
         public Builder setAdminExtras(@NonNull PersistableBundle adminExtras) {
             mAdminExtras = adminExtras != null
                     ? new PersistableBundle(adminExtras)
@@ -333,7 +334,7 @@
                     mLocalTime,
                     mLocale,
                     mDeviceOwnerCanGrantSensorsPermissions,
-                    mAdminExtras,
+                    mAdminExtras != null ? mAdminExtras : new PersistableBundle(),
                     mDemoDevice);
         }
     }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6c6a7ca..fea7770 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -178,6 +178,7 @@
     boolean setProfileOwner(in ComponentName who, String ownerName, int userHandle);
     ComponentName getProfileOwnerAsUser(int userHandle);
     ComponentName getProfileOwnerOrDeviceOwnerSupervisionComponent(in UserHandle userHandle);
+    boolean isSupervisionComponent(in ComponentName who);
     String getProfileOwnerName(int userHandle);
     void setProfileEnabled(in ComponentName who);
     void setProfileName(in ComponentName who, String profileName);
diff --git a/core/java/android/app/admin/ManagedProfileProvisioningParams.java b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
index f91d60a..474f785 100644
--- a/core/java/android/app/admin/ManagedProfileProvisioningParams.java
+++ b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
@@ -252,6 +252,7 @@
          * Sets a {@link Bundle} that contains admin-specific extras.
          */
         @NonNull
+        //TODO(b/235783053) The adminExtras parameter is actually @Nullable.
         public Builder setAdminExtras(@NonNull PersistableBundle adminExtras) {
             mAdminExtras = adminExtras != null
                     ? new PersistableBundle(adminExtras)
@@ -274,7 +275,7 @@
                     mLeaveAllSystemAppsEnabled,
                     mOrganizationOwnedProvisioning,
                     mKeepingAccountOnMigration,
-                    mAdminExtras);
+                    mAdminExtras != null ? mAdminExtras : new PersistableBundle());
         }
     }
 
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 73735ed..0f1b39c 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -892,9 +892,9 @@
      * <tr><th colspan="7">Preview stabilization guaranteed stream configurations</th></tr>
      * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr>
      * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
-     * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code RECORD}</td><td colspan="4" id="rb"></td> <td>Stabilized preview, GPU video processing, or no-preview stabilized video recording.</td> </tr>
-     * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG / YUV}</td><td id="rb">{@code MAXIMUM }</td><td>Standard still imaging with stabilized preview.</td> </tr>
-     * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV / YUV}</td><td id="rb">{@code RECORD }</td><td>High-resolution recording with stabilized preview and recording stream.</td> </tr>
+     * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code s1440p}</td><td colspan="4" id="rb"></td> <td>Stabilized preview, GPU video processing, or no-preview stabilized video recording.</td> </tr>
+     * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code s1440p}</td> <td>{@code JPEG / YUV}</td><td id="rb">{@code MAXIMUM }</td><td>Standard still imaging with stabilized preview.</td> </tr>
+     * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV / YUV}</td><td id="rb">{@code s1440p }</td><td>High-resolution recording with stabilized preview and recording stream.</td> </tr>
      * </table><br>
      * <p>
      * For the maximum size column, PREVIEW refers to the best size match to the device's screen
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index a3bc665..e5b9cdb 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -1265,43 +1265,43 @@
     private static StreamCombinationTemplate sPreviewStabilizedStreamCombinations[] = {
         // 1 stream combinations
         new StreamCombinationTemplate(new StreamTemplate [] {
-                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
                 "Stabilized preview, GPU video processing, or no-preview stabilized recording"),
         new StreamCombinationTemplate(new StreamTemplate [] {
-                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
                 "Stabilized preview, GPU video processing, or no-preview stabilized recording"),
         //2 stream combinations
         new StreamCombinationTemplate(new StreamTemplate [] {
                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
-                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
                 "Standard JPEG still imaging with stabilized preview"),
         new StreamCombinationTemplate(new StreamTemplate [] {
                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
-                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
                 "Standard YUV still imaging with stabilized preview"),
         new StreamCombinationTemplate(new StreamTemplate [] {
                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
-                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
                 "Standard YUV still imaging with stabilized in-app image processing stream"),
         new StreamCombinationTemplate(new StreamTemplate [] {
                 new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
-                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
                 "Standard JPEG still imaging with stabilized in-app image processing stream"),
 
         new StreamCombinationTemplate(new StreamTemplate [] {
-                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p),
                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
                 "High-resolution video recording with preview both streams stabilized"),
         new StreamCombinationTemplate(new StreamTemplate [] {
-                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p),
                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
                 "High-resolution video recording with preview both streams stabilized"),
         new StreamCombinationTemplate(new StreamTemplate [] {
-                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD),
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p),
                 new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
                 "High-resolution video recording with preview both streams stabilized"),
         new StreamCombinationTemplate(new StreamTemplate [] {
-                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD),
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p),
                 new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
                 "High-resolution video recording with preview both streams stabilized"),
     };
@@ -1430,7 +1430,7 @@
             StreamCombinationTemplate []chosenStreamCombinations =
                     sPreviewStabilizedStreamCombinations;
 
-            if (mIsPreviewStabilizationSupported) {
+            if (!mIsPreviewStabilizationSupported) {
                 Log.v(TAG, "Device does not support preview stabilization");
                  return null;
              }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 69c6ba9..b505395 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -559,21 +559,18 @@
      * @see #DISPLAY_CATEGORY_PRESENTATION
      */
     public Display[] getDisplays(String category) {
-        boolean includeDisabledDisplays = (category != null
-                && category.equals(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED));
-        final int[] displayIds = mGlobal.getDisplayIds(includeDisabledDisplays);
+        final int[] displayIds = mGlobal.getDisplayIds();
         synchronized (mLock) {
             try {
-                if (category != null && category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
+                if (category == null
+                        || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
+                    addAllDisplaysLocked(mTempDisplays, displayIds);
+                } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL);
-                } else if ((category == null
-                        || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category))) {
-                    // All displays requested.
-                    addAllDisplaysLocked(mTempDisplays, displayIds);
                 }
                 return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
             } finally {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index da3a580..74356dd 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -206,16 +206,6 @@
      */
     @UnsupportedAppUsage
     public int[] getDisplayIds() {
-        return getDisplayIds(/* includeDisabledDisplays= */ false);
-    }
-
-    /**
-     * Gets all valid logical display ids and invalid ones if specified.
-     *
-     * @return An array containing all display ids.
-     */
-    @UnsupportedAppUsage
-    public int[] getDisplayIds(boolean includeDisabledDisplays) {
         try {
             synchronized (mLock) {
                 if (USE_CACHE) {
@@ -224,8 +214,7 @@
                     }
                 }
 
-                int[] displayIds =
-                        mDm.getDisplayIds(includeDisabledDisplays);
+                int[] displayIds = mDm.getDisplayIds();
                 if (USE_CACHE) {
                     mDisplayIdCache = displayIds;
                 }
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index a4115d1..ca3e580 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -36,7 +36,7 @@
 interface IDisplayManager {
     @UnsupportedAppUsage
     DisplayInfo getDisplayInfo(int displayId);
-    int[] getDisplayIds(boolean includeDisabled);
+    int[] getDisplayIds();
 
     boolean isUidPresentOnDisplay(int uid, int displayId);
 
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index e98d046..a468951 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -223,6 +223,12 @@
     public boolean hasAnimatingParent;
 
     /**
+     * Whether an activity has enabled {@link android.R.styleable#Animation_showBackdrop} for
+     * transition.
+     */
+    public boolean showBackdrop;
+
+    /**
      * The background color of animation in case the task info is not available if the transition
      * is activity level.
      */
@@ -287,6 +293,11 @@
         windowType = in.readInt();
         hasAnimatingParent = in.readBoolean();
         backgroundColor = in.readInt();
+        showBackdrop = in.readBoolean();
+    }
+
+    public void setShowBackdrop(boolean shouldShowBackdrop) {
+        showBackdrop = shouldShowBackdrop;
     }
 
     @Override
@@ -316,6 +327,7 @@
         dest.writeInt(windowType);
         dest.writeBoolean(hasAnimatingParent);
         dest.writeInt(backgroundColor);
+        dest.writeBoolean(showBackdrop);
     }
 
     public void dump(PrintWriter pw, String prefix) {
@@ -337,6 +349,7 @@
         pw.print(prefix); pw.print("windowType="); pw.print(windowType);
         pw.print(prefix); pw.print("hasAnimatingParent="); pw.print(hasAnimatingParent);
         pw.print(prefix); pw.print("backgroundColor="); pw.print(backgroundColor);
+        pw.print(prefix); pw.print("showBackdrop="); pw.print(showBackdrop);
     }
 
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 6829f3d..98d4c59 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4503,10 +4503,13 @@
             for (Map.Entry<HistoryTag, Integer> entry: mHistoryTagPool.entrySet()) {
                 entry.setValue(entry.getValue() | TAG_FIRST_OCCURRENCE_FLAG);
             }
+            // Make a copy of mHistoryCur.
+            HistoryItem copy = new HistoryItem();
+            copy.setTo(cur);
+            // startRecordingHistory will reset mHistoryCur.
             startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
-            HistoryItem newItem = new HistoryItem();
-            newItem.setTo(cur);
-            addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, newItem);
+            // Add the copy into history buffer.
+            addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, copy);
             return;
         }
 
diff --git a/core/proto/android/server/windowmanagertransitiontrace.proto b/core/proto/android/server/windowmanagertransitiontrace.proto
index 9429127..4161f65 100644
--- a/core/proto/android/server/windowmanagertransitiontrace.proto
+++ b/core/proto/android/server/windowmanagertransitiontrace.proto
@@ -58,6 +58,8 @@
   State state = 5;
   int32 flags = 6;
   repeated ChangeInfo change = 7;
+  uint64 start_transaction_id = 8;
+  uint64 finish_transaction_id = 9;
 }
 
 message ChangeInfo {
diff --git a/core/res/res/anim-ldrtl/task_fragment_clear_top_close_enter.xml b/core/res/res/anim-ldrtl/task_fragment_clear_top_close_enter.xml
new file mode 100644
index 0000000..6c44bdc0
--- /dev/null
+++ b/core/res/res/anim-ldrtl/task_fragment_clear_top_close_enter.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false"
+     android:showBackdrop="true">
+    <alpha
+        android:fromAlpha="0.0"
+        android:toAlpha="1.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/linear"
+        android:startOffset="100"
+        android:duration="350" />
+
+    <translate
+        android:fromXDelta="5%"
+        android:toXDelta="0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="450" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_fragment_clear_top_close_exit.xml b/core/res/res/anim-ldrtl/task_fragment_clear_top_close_exit.xml
new file mode 100644
index 0000000..65cf2c2c
--- /dev/null
+++ b/core/res/res/anim-ldrtl/task_fragment_clear_top_close_exit.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false">
+    <alpha
+        android:fromAlpha="1.0"
+        android:toAlpha="0.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/linear"
+        android:startOffset="0"
+        android:duration="100" />
+
+    <translate
+        android:fromXDelta="0"
+        android:toXDelta="-25%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="450" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_fragment_clear_top_open_enter.xml b/core/res/res/anim-ldrtl/task_fragment_clear_top_open_enter.xml
new file mode 100644
index 0000000..022bc22
--- /dev/null
+++ b/core/res/res/anim-ldrtl/task_fragment_clear_top_open_enter.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false"
+     android:showBackdrop="true">
+    <alpha
+        android:fromAlpha="0.0"
+        android:toAlpha="1.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/linear"
+        android:startOffset="100"
+        android:duration="350" />
+
+    <translate
+        android:fromXDelta="-5%"
+        android:toXDelta="0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:duration="450" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_fragment_clear_top_open_exit.xml b/core/res/res/anim-ldrtl/task_fragment_clear_top_open_exit.xml
new file mode 100644
index 0000000..3644dea
--- /dev/null
+++ b/core/res/res/anim-ldrtl/task_fragment_clear_top_open_exit.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false">
+    <alpha
+        android:fromAlpha="1.0"
+        android:toAlpha="0.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/standard_accelerate"
+        android:startOffset="0"
+        android:duration="100" />
+
+    <translate
+        android:fromXDelta="0"
+        android:toXDelta="25%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:duration="450" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_fragment_open_enter.xml b/core/res/res/anim-ldrtl/task_fragment_open_enter.xml
index b6f1af3..9e3780a 100644
--- a/core/res/res/anim-ldrtl/task_fragment_open_enter.xml
+++ b/core/res/res/anim-ldrtl/task_fragment_open_enter.xml
@@ -17,7 +17,7 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:shareInterpolator="false">
+    android:shareInterpolator="false">
 
     <alpha
         android:fromAlpha="0"
diff --git a/core/res/res/anim/task_fragment_clear_top_close_enter.xml b/core/res/res/anim/task_fragment_clear_top_close_enter.xml
new file mode 100644
index 0000000..e33f718
--- /dev/null
+++ b/core/res/res/anim/task_fragment_clear_top_close_enter.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false"
+     android:showBackdrop="true">
+    <alpha
+        android:fromAlpha="0.0"
+        android:toAlpha="1.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/standard_decelerate"
+        android:startOffset="100"
+        android:duration="350" />
+
+    <translate
+        android:fromXDelta="-5%"
+        android:toXDelta="0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="450" />
+
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_fragment_clear_top_close_exit.xml b/core/res/res/anim/task_fragment_clear_top_close_exit.xml
new file mode 100644
index 0000000..3d274ba9
--- /dev/null
+++ b/core/res/res/anim/task_fragment_clear_top_close_exit.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false">
+
+    <alpha
+        android:fromAlpha="1.0"
+        android:toAlpha="0.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/standard_accelerate"
+        android:startOffset="0"
+        android:duration="100" />
+
+    <translate
+        android:fromXDelta="0"
+        android:toXDelta="25%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="450" />
+
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_fragment_clear_top_open_enter.xml b/core/res/res/anim/task_fragment_clear_top_open_enter.xml
new file mode 100644
index 0000000..b963661
--- /dev/null
+++ b/core/res/res/anim/task_fragment_clear_top_open_enter.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false"
+     android:showBackdrop="true">
+
+    <alpha
+        android:fromAlpha="0.0"
+        android:toAlpha="1.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/standard_decelerate"
+        android:startOffset="100"
+        android:duration="350" />
+
+    <translate
+        android:fromXDelta="5%"
+        android:toXDelta="0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:startOffset="0"
+        android:duration="450" />
+
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_fragment_clear_top_open_exit.xml b/core/res/res/anim/task_fragment_clear_top_open_exit.xml
new file mode 100644
index 0000000..22be7d1
--- /dev/null
+++ b/core/res/res/anim/task_fragment_clear_top_open_exit.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false">
+
+    <alpha
+        android:fromAlpha="1.0"
+        android:toAlpha="0.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/standard_accelerate"
+        android:startOffset="0"
+        android:duration="100" />
+
+    <translate
+        android:fromXDelta="0"
+        android:toXDelta="-25%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:duration="450" />
+
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_fragment_open_enter.xml b/core/res/res/anim/task_fragment_open_enter.xml
index aa61e6f..87ee179 100644
--- a/core/res/res/anim/task_fragment_open_enter.xml
+++ b/core/res/res/anim/task_fragment_open_enter.xml
@@ -16,7 +16,7 @@
    -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false">
+     android:shareInterpolator="false">
     <alpha
         android:fromAlpha="0"
         android:toAlpha="1.0"
diff --git a/core/res/res/anim/task_fragment_open_exit.xml b/core/res/res/anim/task_fragment_open_exit.xml
index b4914d2..55a472d 100644
--- a/core/res/res/anim/task_fragment_open_exit.xml
+++ b/core/res/res/anim/task_fragment_open_exit.xml
@@ -32,5 +32,5 @@
         android:fillBefore="true"
         android:fillAfter="true"
         android:interpolator="@interpolator/fast_out_extra_slow_in"
-        android:duration="400" />
-</set>
\ No newline at end of file
+        android:duration="400"/>
+</set>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0817102..1438e7f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1706,6 +1706,10 @@
   <java-symbol type="anim" name="task_fragment_close_exit" />
   <java-symbol type="anim" name="task_fragment_open_enter" />
   <java-symbol type="anim" name="task_fragment_open_exit" />
+  <java-symbol type="anim" name="task_fragment_clear_top_close_enter" />
+  <java-symbol type="anim" name="task_fragment_clear_top_close_exit" />
+  <java-symbol type="anim" name="task_fragment_clear_top_open_enter" />
+  <java-symbol type="anim" name="task_fragment_clear_top_open_exit" />
 
   <java-symbol type="array" name="config_autoRotationTiltTolerance" />
   <java-symbol type="array" name="config_longPressVibePattern" />
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java
index 3858792..dd0e9ff 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java
@@ -154,19 +154,21 @@
 
         for (int i = 0; i < eventCount; i++) {
             String name = "a" + (i % 10);
-            assertThat(iterator.next(item)).isTrue();
-            // Skip a blank event inserted at the start of every buffer
-            if (item.eventCode == BatteryStats.HistoryItem.EVENT_NONE) {
+            do {
                 assertThat(iterator.next(item)).isTrue();
-            }
+                // Skip a blank event inserted at the start of every buffer
+            } while (item.cmd != BatteryStats.HistoryItem.CMD_UPDATE
+                    || item.eventCode == BatteryStats.HistoryItem.EVENT_NONE);
+
             assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_ALARM
                     | BatteryStats.HistoryItem.EVENT_FLAG_START);
             assertThat(item.eventTag.string).isEqualTo(name);
 
-            assertThat(iterator.next(item)).isTrue();
-            if (item.eventCode == BatteryStats.HistoryItem.EVENT_NONE) {
+            do {
                 assertThat(iterator.next(item)).isTrue();
-            }
+            } while (item.cmd != BatteryStats.HistoryItem.CMD_UPDATE
+                    || item.eventCode == BatteryStats.HistoryItem.EVENT_NONE);
+
             assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_ALARM
                     | BatteryStats.HistoryItem.EVENT_FLAG_FINISH);
             assertThat(item.eventTag.string).isEqualTo(name);
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 8d3751e..47f70dd 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -207,8 +207,8 @@
         final Configuration currentConfig = new Configuration();
 
         assertFalse("Must not report change if no public diff",
-                shouldReportChange(0 /* publicDiff */, currentConfig, newConfig,
-                null /* sizeBuckets */, 0 /* handledConfigChanges */));
+                shouldReportChange(currentConfig, newConfig, null /* sizeBuckets */,
+                        0 /* handledConfigChanges */));
 
         final int[] verticalThresholds = {100, 400};
         final SizeConfigurationBuckets buckets = new SizeConfigurationBuckets(
@@ -221,25 +221,25 @@
         newConfig.screenHeightDp = 300;
 
         assertFalse("Must not report changes if the diff is small and not handled",
-                shouldReportChange(CONFIG_SCREEN_SIZE /* publicDiff */, currentConfig,
-                newConfig, buckets, CONFIG_FONT_SCALE /* handledConfigChanges */));
+                shouldReportChange(currentConfig, newConfig, buckets,
+                        CONFIG_FONT_SCALE /* handledConfigChanges */));
 
         assertTrue("Must report changes if the small diff is handled",
-                shouldReportChange(CONFIG_SCREEN_SIZE /* publicDiff */, currentConfig, newConfig,
-                buckets, CONFIG_SCREEN_SIZE /* handledConfigChanges */));
+                shouldReportChange(currentConfig, newConfig, buckets,
+                        CONFIG_SCREEN_SIZE /* handledConfigChanges */));
 
         currentConfig.fontScale = 0.8f;
         newConfig.fontScale = 1.2f;
 
         assertTrue("Must report handled changes regardless of small unhandled change",
-                shouldReportChange(CONFIG_SCREEN_SIZE | CONFIG_FONT_SCALE /* publicDiff */,
-                currentConfig, newConfig, buckets, CONFIG_FONT_SCALE /* handledConfigChanges */));
+                shouldReportChange(currentConfig, newConfig, buckets,
+                        CONFIG_FONT_SCALE /* handledConfigChanges */));
 
         newConfig.screenHeightDp = 500;
 
         assertFalse("Must not report changes if there's unhandled big changes",
-                shouldReportChange(CONFIG_SCREEN_SIZE | CONFIG_FONT_SCALE /* publicDiff */,
-                currentConfig, newConfig, buckets, CONFIG_FONT_SCALE /* handledConfigChanges */));
+                shouldReportChange(currentConfig, newConfig, buckets,
+                        CONFIG_FONT_SCALE /* handledConfigChanges */));
     }
 
     private void recreateAndVerifyNoRelaunch(ActivityThread activityThread, TestActivity activity) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
index 586ac1f..5cc496a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -180,9 +180,18 @@
     Animation loadOpenAnimation(@NonNull RemoteAnimationTarget target,
             @NonNull Rect wholeAnimationBounds) {
         final boolean isEnter = target.mode != MODE_CLOSING;
-        final Animation animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
-                ? com.android.internal.R.anim.task_fragment_open_enter
-                : com.android.internal.R.anim.task_fragment_open_exit);
+        final Animation animation;
+        // Background color on TaskDisplayArea has already been set earlier in
+        // WindowContainer#getAnimationAdapter.
+        if (target.showBackdrop) {
+            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+                    ? com.android.internal.R.anim.task_fragment_clear_top_open_enter
+                    : com.android.internal.R.anim.task_fragment_clear_top_open_exit);
+        } else {
+            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+                    ? com.android.internal.R.anim.task_fragment_open_enter
+                    : com.android.internal.R.anim.task_fragment_open_exit);
+        }
         animation.initialize(target.localBounds.width(), target.localBounds.height(),
                 wholeAnimationBounds.width(), wholeAnimationBounds.height());
         animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
@@ -192,9 +201,16 @@
     Animation loadCloseAnimation(@NonNull RemoteAnimationTarget target,
             @NonNull Rect wholeAnimationBounds) {
         final boolean isEnter = target.mode != MODE_CLOSING;
-        final Animation animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
-                ? com.android.internal.R.anim.task_fragment_close_enter
-                : com.android.internal.R.anim.task_fragment_close_exit);
+        final Animation animation;
+        if (target.showBackdrop) {
+            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+                    ? com.android.internal.R.anim.task_fragment_clear_top_close_enter
+                    : com.android.internal.R.anim.task_fragment_clear_top_close_exit);
+        } else {
+            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+                    ? com.android.internal.R.anim.task_fragment_close_enter
+                    : com.android.internal.R.anim.task_fragment_close_exit);
+        }
         animation.initialize(target.localBounds.width(), target.localBounds.height(),
                 wholeAnimationBounds.width(), wholeAnimationBounds.height());
         animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index ea3712b..a2c4055 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1350,14 +1350,18 @@
                 mStackView.setBubbleSuppressed(update.unsuppressedBubble, false);
             }
 
+            boolean collapseStack = update.expandedChanged && !update.expanded;
+
             // At this point, the correct bubbles are inflated in the stack.
             // Make sure the order in bubble data is reflected in bubble row.
             if (update.orderChanged && mStackView != null) {
                 mDataRepository.addBubbles(mCurrentUserId, update.bubbles);
-                mStackView.updateBubbleOrder(update.bubbles);
+                // if the stack is going to be collapsed, do not update pointer position
+                // after reordering
+                mStackView.updateBubbleOrder(update.bubbles, !collapseStack);
             }
 
-            if (update.expandedChanged && !update.expanded) {
+            if (collapseStack) {
                 mStackView.setExpanded(false);
                 mSysuiProxy.requestNotificationShadeTopUi(false, TAG);
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index b292e1b..2d0be06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1823,7 +1823,7 @@
     /**
      * Update bubble order and pointer position.
      */
-    public void updateBubbleOrder(List<Bubble> bubbles) {
+    public void updateBubbleOrder(List<Bubble> bubbles, boolean updatePointerPositoion) {
         final Runnable reorder = () -> {
             for (int i = 0; i < bubbles.size(); i++) {
                 Bubble bubble = bubbles.get(i);
@@ -1839,7 +1839,10 @@
                     .map(b -> b.getIconView()).collect(Collectors.toList());
             mStackAnimationController.animateReorder(bubbleViews, reorder);
         }
-        updatePointerPosition(false /* forIme */);
+
+        if (updatePointerPositoion) {
+            updatePointerPosition(false /* forIme */);
+        }
     }
 
     /**
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 1724180..920dcc7 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
@@ -216,24 +216,24 @@
 
     /** Applies new configuration, returns {@code false} if there's no effect to the layout. */
     public boolean updateConfiguration(Configuration configuration) {
-        // Always update configuration after orientation changed to make sure to render divider bar
-        // with proper resources that matching screen orientation.
-        final int orientation = configuration.orientation;
-        if (mOrientation != orientation) {
-            mContext = mContext.createConfigurationContext(configuration);
-            mSplitWindowManager.setConfiguration(configuration);
-            mOrientation = orientation;
-        }
-
         // Update the split bounds when necessary. Besides root bounds changed, split bounds need to
         // be updated when the rotation changed to cover the case that users rotated the screen 180
         // degrees.
+        // Make sure to render the divider bar with proper resources that matching the screen
+        // orientation.
         final int rotation = configuration.windowConfiguration.getRotation();
         final Rect rootBounds = configuration.windowConfiguration.getBounds();
-        if (mRotation == rotation && mRootBounds.equals(rootBounds)) {
+        final int orientation = configuration.orientation;
+
+        if (mOrientation == orientation
+                && mRotation == rotation
+                && mRootBounds.equals(rootBounds)) {
             return false;
         }
 
+        mContext = mContext.createConfigurationContext(configuration);
+        mSplitWindowManager.setConfiguration(configuration);
+        mOrientation = orientation;
         mTempRect.set(mRootBounds);
         mRootBounds.set(rootBounds);
         mRotation = rotation;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java
index 530d474..05a024a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.unfold;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
 import android.annotation.NonNull;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.TaskInfo;
@@ -211,6 +213,10 @@
     }
 
     private void resetTask(UnfoldTaskAnimator animator, TaskInfo taskInfo) {
+        if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
+            // PiP task has its own cleanup path, ignore surface reset to avoid conflict.
+            return;
+        }
         final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
         animator.resetSurface(taskInfo, transaction);
         transaction.apply();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java
index 7982089..46de607 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java
@@ -206,6 +206,19 @@
 
     @Test
     public void testApplicableTaskDisappeared_resetsSurface() {
+        mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 0);
+        RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setWindowingMode(0).build();
+        mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+        assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
+
+        mUnfoldAnimationController.onTaskVanished(taskInfo);
+
+        assertThat(mTaskAnimator1.mResetTasks).contains(taskInfo.taskId);
+    }
+
+    @Test
+    public void testApplicablePinnedTaskDisappeared_doesNotResetSurface() {
         mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
         RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
                 .setWindowingMode(2).build();
@@ -214,7 +227,7 @@
 
         mUnfoldAnimationController.onTaskVanished(taskInfo);
 
-        assertThat(mTaskAnimator1.mResetTasks).contains(taskInfo.taskId);
+        assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
     }
 
     @Test
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index 9976fa1..db5e52a 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -429,6 +429,15 @@
     private MediaMetadata(Parcel in) {
         mBundle = in.readBundle();
         mBitmapDimensionLimit = Math.max(in.readInt(), 1);
+
+        // Proactively read bitmaps from known bitmap keys, to ensure that they're unparceled and
+        // added to mBundle's internal map. This ensures that the GC accounts for the underlying
+        // allocations, which it does not do if the bitmaps remain parceled (see b/215820910).
+        // TODO(b/223225532): Remove this workaround once the underlying allocations are properly
+        // tracked in NativeAllocationsRegistry.
+        getBitmap(METADATA_KEY_ART);
+        getBitmap(METADATA_KEY_ALBUM_ART);
+        getBitmap(METADATA_KEY_DISPLAY_ICON);
     }
 
     /**
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
index 9a64b58..ed70ab9 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
@@ -1653,7 +1653,7 @@
                 modeList.contains(CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_OFF));
         checkArrayValuesInRange(key, modes,
                 CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_OFF,
-                CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_ON);
+                CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION);
 
         return modes;
     }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index ce0dda3..1c13b16 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -723,7 +723,7 @@
                   android:excludeFromRecents="true"
                   android:stateNotNeeded="true"
                   android:resumeWhilePausing="true"
-                  android:theme="@style/Theme.AppCompat.DayNight.NoActionBar">
+                  android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
             <intent-filter>
                 <action android:name="android.app.action.CONFIRM_DEVICE_CREDENTIAL_WITH_USER" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml b/packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml
new file mode 100644
index 0000000..1992c77
--- /dev/null
+++ b/packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ 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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/dream_overlay_aqi_unknown" />
+    <corners android:radius="@dimen/dream_aqi_badge_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_biometric_background.xml b/packages/SystemUI/res/layout/auth_biometric_background.xml
index 7ce81ad..995aea1 100644
--- a/packages/SystemUI/res/layout/auth_biometric_background.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_background.xml
@@ -16,6 +16,8 @@
   -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:theme="@style/Theme.AppCompat.DayNight"
+    android:background="?android:colorBackground"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:gravity="center"
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_aqi.xml b/packages/SystemUI/res/layout/dream_overlay_complication_aqi.xml
new file mode 100644
index 0000000..fcebb8d
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_aqi.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ 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.
+  -->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/aqi_view"
+    style="@style/clock_subtitle"
+    android:visibility="gone"
+    android:background="@drawable/dream_aqi_badge_bg"
+    android:paddingHorizontal="@dimen/dream_aqi_badge_padding_horizontal"
+    android:paddingVertical="@dimen/dream_aqi_badge_padding_vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 02ba271..ccf18d2 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -233,4 +233,13 @@
     <color name="connected_network_secondary_color">#41493D</color>
 
     <color name="dream_overlay_camera_mic_off_dot_color">#FCBE03</color>
+
+    <!-- Air Quality -->
+    <color name="dream_overlay_aqi_good">#689F38</color>
+    <color name="dream_overlay_aqi_moderate">#FBC02D</color>
+    <color name="dream_overlay_aqi_unhealthy_sensitive">#F57C00</color>
+    <color name="dream_overlay_aqi_unhealthy">#C53929</color>
+    <color name="dream_overlay_aqi_very_unhealthy">#AD1457</color>
+    <color name="dream_overlay_aqi_hazardous">#880E4F</color>
+    <color name="dream_overlay_aqi_unknown">#BDC1C6</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index ce5f22d..eff4e00 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -727,4 +727,25 @@
         <item>com.android.keyguard</item>
         <item>com.android.systemui</item>
     </string-array>
+
+    <!-- The thresholds which determine the color used by the AQI dream overlay.
+         NOTE: This must always be kept sorted from low to high -->
+    <integer-array name="config_dreamAqiThresholds">
+        <item>-1</item>
+        <item>50</item>
+        <item>100</item>
+        <item>150</item>
+        <item>200</item>
+        <item>300</item>
+    </integer-array>
+
+    <!-- The color values which correspond to the thresholds above -->
+    <integer-array name="config_dreamAqiColorValues">
+        <item>@color/dream_overlay_aqi_good</item>
+        <item>@color/dream_overlay_aqi_moderate</item>
+        <item>@color/dream_overlay_aqi_unhealthy_sensitive</item>
+        <item>@color/dream_overlay_aqi_unhealthy</item>
+        <item>@color/dream_overlay_aqi_very_unhealthy</item>
+        <item>@color/dream_overlay_aqi_hazardous</item>
+    </integer-array>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6a098f5..4d36541 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1503,6 +1503,10 @@
 
     <dimen name="dream_overlay_y_offset">80dp</dimen>
 
+    <dimen name="dream_aqi_badge_corner_radius">28dp</dimen>
+    <dimen name="dream_aqi_badge_padding_vertical">6dp</dimen>
+    <dimen name="dream_aqi_badge_padding_horizontal">16dp</dimen>
+
     <dimen name="status_view_margin_horizontal">0dp</dimen>
 
     <!-- Media output broadcast dialog QR code picture size -->
diff --git a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
index b0530f8..61b1b66 100644
--- a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
@@ -52,15 +52,18 @@
     private var cameraProtectionColor = Color.BLACK
     var faceScanningAnimColor = Utils.getColorAttrDefaultColor(context,
             com.android.systemui.R.attr.wallpaperTextColorAccent)
+    private var cameraProtectionAnimator: ValueAnimator? = null
+    var hideOverlayRunnable: Runnable? = null
+
+    init {
+        visibility = View.INVISIBLE // only show this view when face scanning is happening
+    }
 
     override fun setColor(color: Int) {
         cameraProtectionColor = color
         invalidate()
     }
 
-    private var cameraProtectionAnimator: ValueAnimator? = null
-    var hideOverlayRunnable: Runnable? = null
-
     override fun drawCutoutProtection(canvas: Canvas) {
         if (rimProgress > HIDDEN_RIM_SCALE && !protectionRect.isEmpty) {
             val rimPath = Path(protectionPath)
@@ -96,6 +99,10 @@
         }
     }
 
+    override fun updateVisOnUpdateCutout(): Boolean {
+        return false // instead, we always update the visibility whenever face scanning starts/ends
+    }
+
     override fun enableShowProtection(show: Boolean) {
         val showScanningAnimNow = keyguardUpdateMonitor.isFaceScanning && show
         if (showScanningAnimNow == showScanningAnim) {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index e09c16c..f99293a 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -493,6 +493,13 @@
                         cutoutView.onDisplayChanged(displayId);
                     }
                 }
+
+                DisplayCutoutView overlay = (DisplayCutoutView) getOverlayView(mFaceScanningViewId);
+                if (overlay != null) {
+                    // handle display resolution changes
+                    overlay.onDisplayChanged(displayId);
+                }
+
                 if (mScreenDecorHwcLayer != null) {
                     mScreenDecorHwcLayer.onDisplayChanged(displayId);
                 }
@@ -801,7 +808,7 @@
             });
         }
         // Use visibility of privacy dot views & face scanning view to determine the overlay's
-        // visibility if the screen decoration SW layer overlay isn't persistenly showing
+        // visibility if the screen decoration SW layer overlay isn't persistently showing
         // (ie: rounded corners always showing in SW layer)
         overlay.getRootView().setVisibility(getWindowVisibility(overlay, shouldOptimizeVisibility));
     }
@@ -960,7 +967,7 @@
             viewsMayNeedColorUpdate.add(R.id.rounded_corner_bottom_right);
             viewsMayNeedColorUpdate.add(R.id.display_cutout);
         }
-        if (mFaceScanningFactory.getHasProviders()) {
+        if (getOverlayView(mFaceScanningViewId) != null) {
             viewsMayNeedColorUpdate.add(mFaceScanningViewId);
         }
         final Integer[] views = new Integer[viewsMayNeedColorUpdate.size()];
@@ -1115,14 +1122,12 @@
             updateOverlayProviderViews();
         }
 
-        if (mFaceScanningFactory.getHasProviders()) {
-            FaceScanningOverlay faceScanningOverlay =
-                    (FaceScanningOverlay) getOverlayView(mFaceScanningViewId);
-            if (faceScanningOverlay != null) {
-                faceScanningOverlay.setFaceScanningAnimColor(
-                        Utils.getColorAttrDefaultColor(faceScanningOverlay.getContext(),
-                                com.android.systemui.R.attr.wallpaperTextColorAccent));
-            }
+        FaceScanningOverlay faceScanningOverlay =
+                (FaceScanningOverlay) getOverlayView(mFaceScanningViewId);
+        if (faceScanningOverlay != null) {
+            faceScanningOverlay.setFaceScanningAnimColor(
+                    Utils.getColorAttrDefaultColor(faceScanningOverlay.getContext(),
+                            com.android.systemui.R.attr.wallpaperTextColorAccent));
         }
     }
 
@@ -1342,11 +1347,15 @@
             } else {
                 newVisible = GONE;
             }
-            if (newVisible != getVisibility()) {
+            if (updateVisOnUpdateCutout() && newVisible != getVisibility()) {
                 setVisibility(newVisible);
             }
         }
 
+        protected boolean updateVisOnUpdateCutout() {
+            return true;
+        }
+
         private void updateBoundingPath() {
             final Path path = displayInfo.displayCutout.getCutoutPath();
             if (path != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 41aa112..a097c5e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -492,6 +492,9 @@
         final float scaleFactor = android.util.DisplayUtils.getPhysicalPixelDisplaySizeRatio(
                 mStableDisplaySize.x, mStableDisplaySize.y, displayInfo.getNaturalWidth(),
                 displayInfo.getNaturalHeight());
+        if (scaleFactor == Float.POSITIVE_INFINITY) {
+            return new PointF(mFaceAuthSensorLocation.x, mFaceAuthSensorLocation.y);
+        }
         return new PointF(mFaceAuthSensorLocation.x * scaleFactor,
                 mFaceAuthSensorLocation.y * scaleFactor);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
index 22a7cd1..adc0096 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
@@ -51,15 +51,17 @@
 
     override val hasProviders: Boolean
         get() {
-            // update display info:
+            if (!featureFlags.isEnabled(Flags.FACE_SCANNING_ANIM) ||
+                    authController.faceAuthSensorLocation == null) {
+                return false
+            }
+
+            // update display info
             display?.getDisplayInfo(displayInfo) ?: run {
                 Log.w(TAG, "display is null, can't update displayInfo")
             }
-            val hasDisplayCutout = DisplayCutout.getFillBuiltInDisplayCutout(
+            return DisplayCutout.getFillBuiltInDisplayCutout(
                     context.resources, displayInfo.uniqueId)
-            return hasDisplayCutout &&
-                    authController.faceAuthSensorLocation != null &&
-                    featureFlags.isEnabled(Flags.FACE_SCANNING_ANIM)
         }
 
     override val providers: List<DecorProvider>
@@ -110,7 +112,10 @@
         rotation: Int,
         displayUniqueId: String?
     ) {
-        // no need to handle rotation changes
+        (view.layoutParams as FrameLayout.LayoutParams).let {
+            updateLayoutParams(it, rotation)
+            view.layoutParams = it
+        }
     }
 
     override fun inflateView(
@@ -124,17 +129,40 @@
                 statusBarStateController,
                 keyguardUpdateMonitor)
         view.id = viewId
-        view.visibility = View.INVISIBLE // only show this view when face scanning is happening
-        var heightLayoutParam = ViewGroup.LayoutParams.MATCH_PARENT
-        authController.faceAuthSensorLocation?.y?.let {
-            heightLayoutParam = (it * 3).toInt()
+        FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT).let {
+            updateLayoutParams(it, rotation)
+            parent.addView(view, it)
         }
-        parent.addView(view, FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                heightLayoutParam,
-                Gravity.TOP or Gravity.START))
         return view
     }
+
+    private fun updateLayoutParams(
+        layoutParams: FrameLayout.LayoutParams,
+        @Surface.Rotation rotation: Int
+    ) {
+        layoutParams.let { lp ->
+            lp.width = ViewGroup.LayoutParams.MATCH_PARENT
+            lp.height = ViewGroup.LayoutParams.MATCH_PARENT
+            authController.faceAuthSensorLocation?.y?.let { faceAuthSensorHeight ->
+                val faceScanningHeight = (faceAuthSensorHeight * 2).toInt()
+                when (rotation) {
+                    Surface.ROTATION_0, Surface.ROTATION_180 ->
+                        lp.height = faceScanningHeight
+                    Surface.ROTATION_90, Surface.ROTATION_270 ->
+                        lp.width = faceScanningHeight
+                }
+            }
+
+            lp.gravity = when (rotation) {
+                Surface.ROTATION_0 -> Gravity.TOP or Gravity.START
+                Surface.ROTATION_90 -> Gravity.LEFT or Gravity.START
+                Surface.ROTATION_180 -> Gravity.BOTTOM or Gravity.END
+                Surface.ROTATION_270 -> Gravity.RIGHT or Gravity.END
+                else -> -1 /* invalid rotation */
+            }
+        }
+    }
 }
 
 fun DisplayCutout.getBoundBaseOnCurrentRotation(): List<Int> {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index c8720e4..da6c163 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -183,7 +183,8 @@
                         mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION),
                         null /* setting */,
                         dozeParameters.getPulseOnSigMotion(),
-                        DozeLog.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */,
+                        DozeLog.PULSE_REASON_SENSOR_SIGMOTION,
+                        false /* touchCoords */,
                         false /* touchscreen */),
                 new TriggerSensor(
                         mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
@@ -193,7 +194,8 @@
                         DozeLog.REASON_SENSOR_PICKUP, false /* touchCoords */,
                         false /* touchscreen */,
                         false /* ignoresSetting */,
-                        false /* requires prox */),
+                        false /* requires prox */,
+                        true /* immediatelyReRegister */),
                 new TriggerSensor(
                         findSensor(config.doubleTapSensorType()),
                         Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
@@ -211,6 +213,7 @@
                         true /* touchscreen */,
                         false /* ignoresSetting */,
                         dozeParameters.singleTapUsesProx(mDevicePosture) /* requiresProx */,
+                        true /* immediatelyReRegister */,
                         mDevicePosture),
                 new TriggerSensor(
                         findSensor(config.longPressSensorType()),
@@ -221,7 +224,8 @@
                         true /* reports touch coordinates */,
                         true /* touchscreen */,
                         false /* ignoresSetting */,
-                        dozeParameters.longPressUsesProx() /* requiresProx */),
+                        dozeParameters.longPressUsesProx() /* requiresProx */,
+                        true /* immediatelyReRegister */),
                 new TriggerSensor(
                         findSensor(config.udfpsLongPressSensorType()),
                         "doze_pulse_on_auth",
@@ -231,7 +235,8 @@
                         true /* reports touch coordinates */,
                         true /* touchscreen */,
                         false /* ignoresSetting */,
-                        dozeParameters.longPressUsesProx()),
+                        dozeParameters.longPressUsesProx(),
+                        false /* immediatelyReRegister */),
                 new PluginSensor(
                         new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
                         Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
@@ -257,7 +262,8 @@
                         false /* requiresTouchCoordinates */,
                         false /* requiresTouchscreen */,
                         false /* ignoresSetting */,
-                        false /* requiresProx */),
+                        false /* requiresProx */,
+                        true /* immediatelyReRegister */),
         };
         setProxListening(false);  // Don't immediately start listening when we register.
         mProximitySensor.register(
@@ -493,6 +499,10 @@
         private final boolean mRequiresTouchscreen;
         private final boolean mRequiresProx;
 
+        // Whether to immediately re-register this sensor after the sensor is triggered.
+        // If false, the sensor registration will be updated on the next AOD state transition.
+        private final boolean mImmediatelyReRegister;
+
         protected boolean mRequested;
         protected boolean mRegistered;
         protected boolean mDisabled;
@@ -516,7 +526,8 @@
                     reportsTouchCoordinates,
                     requiresTouchscreen,
                     false /* ignoresSetting */,
-                    false /* requiresProx */
+                    false /* requiresProx */,
+                    true /* immediatelyReRegister */
             );
         }
 
@@ -529,7 +540,8 @@
                 boolean reportsTouchCoordinates,
                 boolean requiresTouchscreen,
                 boolean ignoresSetting,
-                boolean requiresProx
+                boolean requiresProx,
+                boolean immediatelyReRegister
         ) {
             this(
                     new Sensor[]{ sensor },
@@ -541,6 +553,7 @@
                     requiresTouchscreen,
                     ignoresSetting,
                     requiresProx,
+                    immediatelyReRegister,
                     DevicePostureController.DEVICE_POSTURE_UNKNOWN
             );
         }
@@ -555,6 +568,7 @@
                 boolean requiresTouchscreen,
                 boolean ignoresSetting,
                 boolean requiresProx,
+                boolean immediatelyReRegister,
                 @DevicePostureController.DevicePostureInt int posture
         ) {
             mSensors = sensors;
@@ -567,6 +581,7 @@
             mIgnoresSetting = ignoresSetting;
             mRequiresProx = requiresProx;
             mPosture = posture;
+            mImmediatelyReRegister = immediatelyReRegister;
         }
 
         /**
@@ -702,8 +717,8 @@
                     screenY = event.values[1];
                 }
                 mSensorCallback.onSensorPulse(mPulseReason, screenX, screenY, event.values);
-                if (!mRegistered) {
-                    updateListening();  // reregister, this sensor only fires once
+                if (!mRegistered && mImmediatelyReRegister) {
+                    updateListening();
                 }
             }));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayNotificationCountProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayNotificationCountProvider.java
index 3dd4386..f9fc1f3 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayNotificationCountProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayNotificationCountProvider.java
@@ -20,7 +20,6 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 
-import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
@@ -33,13 +32,10 @@
 import java.util.Set;
 import java.util.concurrent.Executor;
 
-import javax.inject.Inject;
-
 /***
  * {@link DreamOverlayNotificationCountProvider} provides the current notification count to
  * registered callbacks. Ongoing notifications are not included in the count.
  */
-@SysUISingleton
 public class DreamOverlayNotificationCountProvider
         implements CallbackController<DreamOverlayNotificationCountProvider.Callback> {
     private final Set<String> mNotificationKeys = new HashSet<>();
@@ -82,7 +78,6 @@
         }
     };
 
-    @Inject
     public DreamOverlayNotificationCountProvider(
             NotificationListener notificationListener,
             @Background Executor bgExecutor) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index e878b22..250313d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -49,6 +49,7 @@
 
 import java.util.Locale;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -65,7 +66,8 @@
     private final Resources mResources;
     private final DateFormatUtil mDateFormatUtil;
     private final IndividualSensorPrivacyController mSensorPrivacyController;
-    private final DreamOverlayNotificationCountProvider mDreamOverlayNotificationCountProvider;
+    private final Optional<DreamOverlayNotificationCountProvider>
+            mDreamOverlayNotificationCountProvider;
     private final ZenModeController mZenModeController;
     private final Executor mMainExecutor;
 
@@ -125,7 +127,7 @@
             NextAlarmController nextAlarmController,
             DateFormatUtil dateFormatUtil,
             IndividualSensorPrivacyController sensorPrivacyController,
-            DreamOverlayNotificationCountProvider dreamOverlayNotificationCountProvider,
+            Optional<DreamOverlayNotificationCountProvider> dreamOverlayNotificationCountProvider,
             ZenModeController zenModeController,
             StatusBarWindowStateController statusBarWindowStateController) {
         super(view);
@@ -161,7 +163,9 @@
         mZenModeController.addCallback(mZenModeCallback);
         updatePriorityModeStatusIcon();
 
-        mDreamOverlayNotificationCountProvider.addCallback(mNotificationCountCallback);
+        mDreamOverlayNotificationCountProvider.ifPresent(
+                provider -> provider.addCallback(mNotificationCountCallback));
+
         mTouchInsetSession.addViewToTracking(mView);
     }
 
@@ -171,7 +175,8 @@
         mSensorPrivacyController.removeCallback(mSensorCallback);
         mNextAlarmController.removeCallback(mNextAlarmCallback);
         mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
-        mDreamOverlayNotificationCountProvider.removeCallback(mNotificationCountCallback);
+        mDreamOverlayNotificationCountProvider.ifPresent(
+                provider -> provider.removeCallback(mNotificationCountCallback));
         mTouchInsetSession.clear();
 
         mIsAttached = false;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/AirQualityColorPicker.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/AirQualityColorPicker.java
new file mode 100644
index 0000000..328753f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/AirQualityColorPicker.java
@@ -0,0 +1,70 @@
+/*
+ * 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.systemui.dreams.complication;
+
+import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COLOR_DEFAULT;
+import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COLOR_THRESHOLDS;
+import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COLOR_VALUES;
+
+import android.util.Log;
+
+import androidx.annotation.ColorInt;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+final class AirQualityColorPicker {
+    private static final String TAG = "AirQualityColorPicker";
+    private final int[] mThresholds;
+    private final int[] mColorValues;
+    private final int mDefaultColor;
+
+    @Inject
+    AirQualityColorPicker(@Named(DREAM_AQI_COLOR_THRESHOLDS) int[] thresholds,
+            @Named(DREAM_AQI_COLOR_VALUES) int[] colorValues,
+            @Named(DREAM_AQI_COLOR_DEFAULT) @ColorInt int defaultColor) {
+        mThresholds = thresholds;
+        mColorValues = colorValues;
+        mDefaultColor = defaultColor;
+    }
+
+    @ColorInt
+    int getColorForValue(String aqiString) {
+        int size = mThresholds.length;
+        if (mThresholds.length != mColorValues.length) {
+            size = Math.min(mThresholds.length, mColorValues.length);
+            Log.e(TAG,
+                    "Threshold size ("
+                            + mThresholds.length + ") does not match color value size ("
+                            + mColorValues.length
+                            + "). Taking the minimum, some values may be ignored.");
+
+        }
+        try {
+            final int value = Integer.parseInt(aqiString.replaceAll("[^0-9]", ""));
+            for (int i = size - 1; i >= 0; i--) {
+                if (value > mThresholds[i]) {
+                    return mColorValues[i];
+                }
+            }
+            Log.e(TAG, "No matching AQI color for value: " + value);
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "Could not read AQI value from:" + aqiString);
+        }
+        return mDefaultColor;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamAirQualityComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamAirQualityComplication.java
new file mode 100644
index 0000000..ba63303
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamAirQualityComplication.java
@@ -0,0 +1,199 @@
+/*
+ * 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.systemui.dreams.complication;
+
+import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COMPLICATION_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COMPLICATION_VIEW;
+import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT;
+
+import android.app.smartspace.SmartspaceAction;
+import android.app.smartspace.SmartspaceTarget;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent;
+import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Air quality complication which produces view holder responsible for showing AQI over dreams.
+ */
+public class DreamAirQualityComplication implements Complication {
+    // TODO(b/236024839): Move to SmartspaceTarget
+    public static final int FEATURE_AIR_QUALITY = 46;
+
+    private final DreamAirQualityComplicationComponent.Factory mComponentFactory;
+
+    @Inject
+    public DreamAirQualityComplication(
+            DreamAirQualityComplicationComponent.Factory componentFactory) {
+        mComponentFactory = componentFactory;
+    }
+
+    @Override
+    public int getRequiredTypeAvailability() {
+        return COMPLICATION_TYPE_AIR_QUALITY;
+    }
+
+    @Override
+    public ViewHolder createView(ComplicationViewModel model) {
+        return mComponentFactory.create().getViewHolder();
+    }
+
+    /**
+     * {@link CoreStartable} for registering {@link DreamAirQualityComplication} with SystemUI.
+     */
+    public static class Registrant extends CoreStartable {
+        private final DreamOverlayStateController mDreamOverlayStateController;
+        private final DreamAirQualityComplication mComplication;
+
+        /**
+         * Default constructor to register {@link DreamAirQualityComplication}.
+         */
+        @Inject
+        public Registrant(Context context,
+                DreamOverlayStateController dreamOverlayStateController,
+                DreamAirQualityComplication complication) {
+            super(context);
+            mDreamOverlayStateController = dreamOverlayStateController;
+            mComplication = complication;
+        }
+
+        @Override
+        public void start() {
+            // TODO(b/221500478): Only add complication once we have data to show.
+            mDreamOverlayStateController.addComplication(mComplication);
+        }
+    }
+
+    /**
+     * ViewHolder to contain value/logic associated with the AQI complication view.
+     */
+    public static class DreamAirQualityViewHolder implements ViewHolder {
+        private final TextView mView;
+        private final DreamAirQualityViewController mController;
+        private final ComplicationLayoutParams mLayoutParams;
+
+        @Inject
+        DreamAirQualityViewHolder(@Named(DREAM_AQI_COMPLICATION_VIEW) TextView view,
+                DreamAirQualityViewController controller,
+                @Named(DREAM_AQI_COMPLICATION_LAYOUT_PARAMS)
+                        ComplicationLayoutParams layoutParams) {
+            mView = view;
+            mLayoutParams = layoutParams;
+            mController = controller;
+            mController.init();
+        }
+
+        @Override
+        public View getView() {
+            return mView;
+        }
+
+        @Override
+        public ComplicationLayoutParams getLayoutParams() {
+            return mLayoutParams;
+        }
+    }
+
+    static class DreamAirQualityViewController extends ViewController<TextView> {
+        private final DreamSmartspaceController mSmartspaceController;
+        private final String mSmartspaceTrampolineComponent;
+        private final ActivityStarter mActivityStarter;
+        private final AirQualityColorPicker mAirQualityColorPicker;
+
+        private final SmartspaceTargetListener mSmartspaceTargetListener = targets -> {
+            final SmartspaceTarget target = targets.stream()
+                    .filter(t -> t instanceof SmartspaceTarget)
+                    .map(t -> (SmartspaceTarget) t)
+                    .filter(t -> t.getFeatureType() == FEATURE_AIR_QUALITY)
+                    .findFirst()
+                    .orElse(null);
+            updateView(target);
+        };
+
+        @Inject
+        DreamAirQualityViewController(@Named(DREAM_AQI_COMPLICATION_VIEW) TextView view,
+                DreamSmartspaceController smartspaceController,
+                @Named(SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT)
+                        String smartspaceTrampolineComponent,
+                ActivityStarter activityStarter,
+                AirQualityColorPicker airQualityColorPicker) {
+            super(view);
+            mSmartspaceController = smartspaceController;
+            mSmartspaceTrampolineComponent = smartspaceTrampolineComponent;
+            mActivityStarter = activityStarter;
+            mAirQualityColorPicker = airQualityColorPicker;
+        }
+
+        @Override
+        protected void onViewAttached() {
+            mSmartspaceController.addUnfilteredListener(mSmartspaceTargetListener);
+        }
+
+        @Override
+        protected void onViewDetached() {
+            mSmartspaceController.removeUnfilteredListener(mSmartspaceTargetListener);
+        }
+
+        private void updateView(@Nullable SmartspaceTarget target) {
+            final SmartspaceAction headerAction = target == null ? null : target.getHeaderAction();
+            if (headerAction == null || TextUtils.isEmpty(headerAction.getTitle())) {
+                mView.setVisibility(View.GONE);
+                return;
+            }
+            mView.setVisibility(View.VISIBLE);
+
+            final String airQuality = headerAction.getTitle().toString();
+            mView.setText(airQuality);
+
+            final Drawable background = mView.getBackground().mutate();
+            final int color = mAirQualityColorPicker.getColorForValue(airQuality);
+
+            if (background instanceof ShapeDrawable) {
+                ((ShapeDrawable) background).getPaint().setColor(color);
+            } else if (background instanceof GradientDrawable) {
+                ((GradientDrawable) background).setColor(color);
+            }
+            mView.setBackground(background);
+
+            final Intent intent = headerAction.getIntent();
+            if (intent != null && intent.getComponent() != null
+                    && intent.getComponent().getClassName().equals(
+                    mSmartspaceTrampolineComponent)) {
+                mView.setOnClickListener(v -> {
+                    mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0);
+                });
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java
index aceafb5..4eae3b92 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java
@@ -18,7 +18,7 @@
 
 import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS;
 import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_VIEW;
-import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT;
+import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT;
 
 import android.app.smartspace.SmartspaceAction;
 import android.app.smartspace.SmartspaceTarget;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamAirQualityComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamAirQualityComplicationComponent.java
new file mode 100644
index 0000000..112a1ce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamAirQualityComplicationComponent.java
@@ -0,0 +1,137 @@
+/*
+ * 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.systemui.dreams.complication.dagger;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.ColorInt;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.complication.ComplicationLayoutParams;
+import com.android.systemui.dreams.complication.DreamAirQualityComplication;
+import com.android.systemui.dreams.complication.DreamAirQualityComplication.DreamAirQualityViewHolder;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * Component responsible for generating dependencies for the {@link DreamAirQualityComplication},
+ * such as the layout details.
+ */
+@Subcomponent(modules = {
+        DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.class,
+})
+@DreamAirQualityComplicationComponent.DreamAirQualityComplicationScope
+public interface DreamAirQualityComplicationComponent {
+
+    @Documented
+    @Retention(RetentionPolicy.RUNTIME)
+    @Scope
+    @interface DreamAirQualityComplicationScope {
+    }
+
+    /**
+     * Generates {@link DreamAirQualityComplicationComponent}.
+     */
+    @Subcomponent.Factory
+    interface Factory {
+        DreamAirQualityComplicationComponent create();
+    }
+
+    /**
+     * Creates {@link DreamAirQualityViewHolder}.
+     */
+    DreamAirQualityViewHolder getViewHolder();
+
+    /**
+     * Scoped values for {@link DreamAirQualityComplicationComponent}.
+     */
+    @Module
+    interface DreamAirQualityComplicationModule {
+        String DREAM_AQI_COMPLICATION_VIEW = "aqi_complication_view";
+        String DREAM_AQI_COMPLICATION_LAYOUT_PARAMS = "aqi_complication_layout_params";
+        String DREAM_AQI_COLOR_THRESHOLDS = "aqi_color_thresholds";
+        String DREAM_AQI_COLOR_VALUES = "aqi_color_values";
+        String DREAM_AQI_COLOR_DEFAULT = "aqi_color_default";
+        // Order weight of insert into parent container
+        int INSERT_ORDER_WEIGHT = 1;
+
+        /**
+         * Provides the complication view.
+         */
+        @Provides
+        @DreamAirQualityComplicationScope
+        @Named(DREAM_AQI_COMPLICATION_VIEW)
+        static TextView provideComplicationView(LayoutInflater layoutInflater) {
+            return Objects.requireNonNull((TextView)
+                            layoutInflater.inflate(R.layout.dream_overlay_complication_aqi,
+                                    null, false),
+                    "R.layout.dream_overlay_complication_aqi did not properly inflated");
+        }
+
+        /**
+         * Provides the layout parameters for the complication view.
+         */
+        @Provides
+        @DreamAirQualityComplicationScope
+        @Named(DREAM_AQI_COMPLICATION_LAYOUT_PARAMS)
+        static ComplicationLayoutParams provideLayoutParams() {
+            return new ComplicationLayoutParams(0,
+                    ViewGroup.LayoutParams.WRAP_CONTENT,
+                    ComplicationLayoutParams.POSITION_BOTTOM
+                            | ComplicationLayoutParams.POSITION_START,
+                    ComplicationLayoutParams.DIRECTION_END,
+                    INSERT_ORDER_WEIGHT, /* snapToGuide= */ true);
+        }
+
+        @Provides
+        @DreamAirQualityComplicationScope
+        @Named(DREAM_AQI_COLOR_THRESHOLDS)
+        static int[] provideAqiColorThresholds(@Main Resources resources) {
+            return resources.getIntArray(R.array.config_dreamAqiThresholds);
+        }
+
+        @Provides
+        @DreamAirQualityComplicationScope
+        @Named(DREAM_AQI_COLOR_VALUES)
+        static int[] provideAqiColorValues(@Main Resources resources) {
+            return resources.getIntArray(R.array.config_dreamAqiColorValues);
+        }
+
+        @Provides
+        @DreamAirQualityComplicationScope
+        @Named(DREAM_AQI_COLOR_DEFAULT)
+        @ColorInt
+        static int provideDefaultAqiColor(Context context) {
+            return context.getColor(R.color.dream_overlay_aqi_unknown);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationModule.java
index eb2fc5d..3ab26ce 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationModule.java
@@ -41,7 +41,7 @@
             "clock_date_complication_layout_params";
     // Order weight of insert into parent container
     //TODO(b/217199227): move to a single location.
-    int INSERT_ORDER_WEIGHT = 2;
+    int INSERT_ORDER_WEIGHT = 3;
 
     /**
      * Provides the complication view.
@@ -66,6 +66,6 @@
                 ComplicationLayoutParams.POSITION_BOTTOM
                         | ComplicationLayoutParams.POSITION_START,
                 ComplicationLayoutParams.DIRECTION_END,
-                INSERT_ORDER_WEIGHT);
+                INSERT_ORDER_WEIGHT, /* snapToGuide= */ true);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java
index a1660f2..7ab3ad1 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java
@@ -19,7 +19,6 @@
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
-import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 import android.widget.TextView;
@@ -77,9 +76,8 @@
         String DREAM_WEATHER_COMPLICATION_VIEW = "weather_complication_view";
         String DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS =
                 "weather_complication_layout_params";
-        String SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT = "smartspace_trampoline_activity";
         // Order weight of insert into parent container
-        int INSERT_ORDER_WEIGHT = 1;
+        int INSERT_ORDER_WEIGHT = 2;
 
         /**
          * Provides the complication view.
@@ -106,17 +104,7 @@
                     ComplicationLayoutParams.POSITION_BOTTOM
                             | ComplicationLayoutParams.POSITION_START,
                     ComplicationLayoutParams.DIRECTION_END,
-                    INSERT_ORDER_WEIGHT);
-        }
-
-        /**
-         * Provides the smartspace trampoline activity component.
-         */
-        @Provides
-        @DreamWeatherComplicationScope
-        @Named(SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT)
-        static String provideSmartspaceTrampolineActivityComponent(Context context) {
-            return context.getString(R.string.config_smartspaceTrampolineActivityComponent);
+                    INSERT_ORDER_WEIGHT, /* snapToGuide= */ true);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
index 62a4140c..98344aa 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
@@ -16,9 +16,15 @@
 
 package com.android.systemui.dreams.complication.dagger;
 
+import android.content.Context;
+
+import com.android.systemui.R;
 import com.android.systemui.dagger.SystemUIBinder;
 
+import javax.inject.Named;
+
 import dagger.Module;
+import dagger.Provides;
 
 /**
  * Module for all components with corresponding dream layer complications registered in
@@ -30,6 +36,17 @@
         },
         subcomponents = {
                 DreamWeatherComplicationComponent.class,
+                DreamAirQualityComplicationComponent.class,
         })
 public interface RegisteredComplicationsModule {
+    String SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT = "smartspace_trampoline_activity";
+
+    /**
+     * Provides the smartspace trampoline activity component.
+     */
+    @Provides
+    @Named(SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT)
+    static String provideSmartspaceTrampolineActivityComponent(Context context) {
+        return context.getString(R.string.config_smartspaceTrampolineActivityComponent);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index cd23f14..2dd2098 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -20,9 +20,13 @@
 import android.content.res.Resources;
 
 import com.android.settingslib.dream.DreamBackend;
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.DreamOverlayNotificationCountProvider;
 import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule;
 
+import java.util.Optional;
+
 import javax.inject.Named;
 
 import dagger.Module;
@@ -50,6 +54,18 @@
         return DreamBackend.getInstance(context);
     }
 
+    /**
+     * Provides an instance of a {@link DreamOverlayNotificationCountProvider}.
+     */
+    @SysUISingleton
+    @Provides
+    static Optional<DreamOverlayNotificationCountProvider>
+            providesDreamOverlayNotificationCountProvider() {
+        // If we decide to bring this back, we should gate it on a config that can be changed in
+        // an overlay.
+        return Optional.empty();
+    }
+
     /** */
     @Provides
     @Named(DREAM_ONLY_ENABLED_FOR_SYSTEM_USER)
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index f86541a..47c678b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -45,9 +45,6 @@
 
     /***************************************/
     // 100 - notification
-    public static final BooleanFlag NEW_NOTIFICATION_PIPELINE_RENDERING =
-            new BooleanFlag(101, true);
-
     public static final BooleanFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
             new BooleanFlag(103, false);
 
@@ -194,6 +191,8 @@
     public static final SysPropBooleanFlag WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
             new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false);
 
+    public static final BooleanFlag NEW_BACK_AFFORDANCE = new BooleanFlag(1203, true);
+
     // Pay no attention to the reflection behind the curtain.
     // ========================== Curtain ==========================
     // |                                                           |
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index c686b48..99b5720 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -169,7 +169,8 @@
             playingCannedAnimation: Boolean,
             fromWakeAndUnlock: Boolean,
             unlockAnimationStartDelay: Long,
-            unlockAnimationDuration: Long) {}
+            unlockAnimationDuration: Long
+        ) {}
 
         /**
          * Called when the remote unlock animation ends, in all cases, canned or swipe-to-unlock.
@@ -308,8 +309,12 @@
                     // call onKeyguardExitRemoteAnimationFinished since that will hide the keyguard
                     // and unlock the device as well as hiding the surface.
                     if (surfaceBehindAlpha == 0f) {
+                        Log.d(TAG, "surfaceBehindAlphaAnimator#onAnimationEnd")
                         keyguardViewMediator.get().finishSurfaceBehindRemoteAnimation(
                             false /* cancelled */)
+                    } else {
+                        Log.d(TAG, "skip finishSurfaceBehindRemoteAnimation" +
+                                " surfaceBehindAlpha=$surfaceBehindAlpha")
                     }
                 }
             })
@@ -325,6 +330,7 @@
             }
             addListener(object : AnimatorListenerAdapter() {
                 override fun onAnimationEnd(animation: Animator) {
+                    Log.d(TAG, "surfaceBehindEntryAnimator#onAnimationEnd")
                     playingCannedUnlockAnimation = false
                     keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
                         false /* cancelled */
@@ -372,8 +378,8 @@
      * changed.
      */
     override fun onKeyguardGoingAwayChanged() {
-        if (keyguardStateController.isKeyguardGoingAway
-            && !statusBarStateController.leaveOpenOnKeyguardHide()) {
+        if (keyguardStateController.isKeyguardGoingAway &&
+                !statusBarStateController.leaveOpenOnKeyguardHide()) {
             prepareForInWindowLauncherAnimations()
         }
     }
@@ -427,7 +433,7 @@
             launcherUnlockController?.prepareForUnlock(
                 willUnlockWithSmartspaceTransition, /* willAnimateSmartspace */
                 lockscreenSmartspaceBounds, /* lockscreenSmartspaceBounds */
-                selectedPage, /* selectedPage */
+                selectedPage /* selectedPage */
             )
         } catch (e: RemoteException) {
             Log.e(TAG, "Remote exception in prepareForInWindowUnlockAnimations.", e)
@@ -502,19 +508,23 @@
      * by the dismiss amount via [onKeyguardDismissAmountChanged].
      */
     private fun playCannedUnlockAnimation() {
+        Log.d(TAG, "playCannedUnlockAnimation")
         playingCannedUnlockAnimation = true
 
-
         when {
             // If we're set up for in-window launcher animations, ask Launcher to play its in-window
             // canned animation.
-            willUnlockWithInWindowLauncherAnimations -> unlockToLauncherWithInWindowAnimations()
+            willUnlockWithInWindowLauncherAnimations -> {
+                Log.d(TAG, "playCannedUnlockAnimation, unlockToLauncherWithInWindowAnimations")
+                unlockToLauncherWithInWindowAnimations()
+            }
 
             // If we're waking and unlocking to a non-Launcher app surface (or Launcher in-window
             // animations are not available), show it immediately and end the remote animation. The
             // circular light reveal will show the app surface, and it looks weird if it's moving
             // around behind that.
             biometricUnlockControllerLazy.get().isWakeAndUnlock -> {
+                Log.d(TAG, "playCannedUnlockAnimation, isWakeAndUnlock")
                 setSurfaceBehindAppearAmount(1f)
                 keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
                     false /* cancelled */)
@@ -522,7 +532,10 @@
 
             // Otherwise, we're doing a normal full-window unlock. Start this animator, which will
             // scale/translate the window underneath the lockscreen.
-            else -> surfaceBehindEntryAnimator.start()
+            else -> {
+                Log.d(TAG, "playCannedUnlockAnimation, surfaceBehindEntryAnimator#start")
+                surfaceBehindEntryAnimator.start()
+            }
         }
     }
 
@@ -774,16 +787,17 @@
     }
 
     private fun fadeInSurfaceBehind() {
+        Log.d(TAG, "fadeInSurfaceBehind")
         surfaceBehindAlphaAnimator.cancel()
         surfaceBehindAlphaAnimator.start()
     }
 
     private fun fadeOutSurfaceBehind() {
+        Log.d(TAG, "fadeOutSurfaceBehind")
         surfaceBehindAlphaAnimator.cancel()
         surfaceBehindAlphaAnimator.reverse()
     }
 
-
     private fun shouldPerformSmartspaceTransition(): Boolean {
         // Feature is disabled, so we don't want to.
         if (!featureFlags.isEnabled(Flags.SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED)) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c5f6b78..f9a1c66 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -486,6 +486,8 @@
      */
     private IRemoteAnimationRunner mKeyguardExitAnimationRunner;
 
+    private CentralSurfaces mCentralSurfaces;
+
     private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
             new DeviceConfig.OnPropertiesChangedListener() {
             @Override
@@ -846,6 +848,13 @@
                             + mOccluded);
                 }
 
+                @Override
+                public void onLaunchAnimationEnd(boolean launchIsFullScreen) {
+                    if (launchIsFullScreen) {
+                        mCentralSurfaces.instantCollapseNotificationPanel();
+                    }
+                }
+
                 @NonNull
                 @Override
                 public ViewGroup getLaunchContainer() {
@@ -2421,7 +2430,7 @@
             RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
             RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
         Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
-        if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
+        Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
                 + " fadeoutDuration=" + fadeoutDuration);
         synchronized (KeyguardViewMediator.this) {
 
@@ -2617,7 +2626,11 @@
      * @param cancelled {@code true} if the animation was cancelled before it finishes.
      */
     public void onKeyguardExitRemoteAnimationFinished(boolean cancelled) {
+        Log.d(TAG, "onKeyguardExitRemoteAnimationFinished");
         if (!mSurfaceBehindRemoteAnimationRunning && !mSurfaceBehindRemoteAnimationRequested) {
+            Log.d(TAG, "skip onKeyguardExitRemoteAnimationFinished cancelled=" + cancelled
+                    + " surfaceAnimationRunning=" + mSurfaceBehindRemoteAnimationRunning
+                    + " surfaceAnimationRequested=" + mSurfaceBehindRemoteAnimationRequested);
             return;
         }
 
@@ -2631,7 +2644,13 @@
             onKeyguardExitFinished();
 
             if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) {
+                Log.d(TAG, "onKeyguardExitRemoteAnimationFinished"
+                        + "#hideKeyguardViewAfterRemoteAnimation");
                 mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation();
+            } else {
+                Log.d(TAG, "skip hideKeyguardViewAfterRemoteAnimation"
+                        + " dismissFromSwipe=" + mKeyguardStateController.isDismissingFromSwipe()
+                        + " wasShowing=" + wasShowing);
             }
 
             finishSurfaceBehindRemoteAnimation(cancelled);
@@ -2853,6 +2872,7 @@
             @Nullable PanelExpansionStateManager panelExpansionStateManager,
             BiometricUnlockController biometricUnlockController,
             View notificationContainer, KeyguardBypassController bypassController) {
+        mCentralSurfaces = centralSurfaces;
         mKeyguardViewControllerLazy.get().registerCentralSurfaces(
                 centralSurfaces,
                 panelView,
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 05da4bb..90cca15 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -52,7 +52,7 @@
         return factory.create("NotifLog", 1000 /* maxSize */, false /* systrace */);
     }
 
-    /** Provides a logging buffer for all logs related to the data layer of notifications. */
+    /** Provides a logging buffer for logs related to heads up presentation of notifications. */
     @Provides
     @SysUISingleton
     @NotificationHeadsUpLog
@@ -60,6 +60,14 @@
         return factory.create("NotifHeadsUpLog", 1000);
     }
 
+    /** Provides a logging buffer for notification interruption calculations. */
+    @Provides
+    @SysUISingleton
+    @NotificationInterruptLog
+    public static LogBuffer provideNotificationInterruptLogBuffer(LogBufferFactory factory) {
+        return factory.create("NotifInterruptLog", 100);
+    }
+
     /** Provides a logging buffer for all logs for lockscreen to shade transition events. */
     @Provides
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationInterruptLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationInterruptLog.java
new file mode 100644
index 0000000..760fbf3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationInterruptLog.java
@@ -0,0 +1,33 @@
+/*
+ * 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.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for notification interruption logging. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface NotificationInterruptLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index e43ae0f..68aba62 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -725,6 +725,7 @@
                         scaleTransitionDrawableLayer(transitionDrawable, 1, width, height);
                         transitionDrawable.setLayerGravity(0, Gravity.CENTER);
                         transitionDrawable.setLayerGravity(1, Gravity.CENTER);
+                        transitionDrawable.setCrossFadeEnabled(!isArtworkBound);
 
                         albumView.setImageDrawable(transitionDrawable);
                         transitionDrawable.startTransition(isArtworkBound ? 333 : 80);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index b2ab12a..5b2cda0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -219,4 +219,21 @@
 
     /** Whether or not to show the broadcast button */
     val showBroadcastButton: Boolean
-)
+) {
+    /**
+     * Check whether [MediaDeviceData] objects are equal in all fields except the icon. The icon
+     * is ignored because it can change by reference frequently depending on the device type's
+     * implementation, but this is not usually relevant unless other info has changed
+     */
+    fun equalsWithoutIcon(other: MediaDeviceData?): Boolean {
+        if (other == null) {
+            return false
+        }
+
+        return enabled == other.enabled &&
+            name == other.name &&
+            intent == other.intent &&
+            id == other.id &&
+            showBroadcastButton == other.showBroadcastButton
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index b552d9f..8305050 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -168,8 +168,8 @@
         private var playbackType = PLAYBACK_TYPE_UNKNOWN
         private var current: MediaDeviceData? = null
             set(value) {
-                val hasSameId = value?.id != null && value.id == field?.id
-                if (!started || (!hasSameId && value != field)) {
+                val sameWithoutIcon = value != null && value.equalsWithoutIcon(field)
+                if (!started || !sameWithoutIcon) {
                     field = value
                     fgExecutor.execute {
                         processDevice(key, oldKey, value)
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index d41837b..e210d68 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -234,7 +234,7 @@
     private boolean mIsBackGestureAllowed;
     private boolean mGestureBlockingActivityRunning;
     private boolean mIsInPipMode;
-    private boolean mIsPredictiveBackAnimEnabled;
+    private boolean mIsNewBackAffordanceEnabled;
 
     private InputMonitor mInputMonitor;
     private InputChannelCompat.InputEventReceiver mInputEventReceiver;
@@ -524,8 +524,7 @@
                     Choreographer.getInstance(), this::onInputEvent);
 
             // Add a nav bar panel window
-            mIsPredictiveBackAnimEnabled =
-                    mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_ANIM);
+            mIsNewBackAffordanceEnabled = mFeatureFlags.isEnabled(Flags.NEW_BACK_AFFORDANCE);
             resetEdgeBackPlugin();
             mPluginManager.addPluginListener(
                     this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
@@ -545,7 +544,7 @@
     }
 
     private void resetEdgeBackPlugin() {
-        if (mIsPredictiveBackAnimEnabled) {
+        if (mIsNewBackAffordanceEnabled) {
             setEdgeBackPlugin(
                     mBackPanelControllerFactory.create(mContext, mBackAnimation));
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
index 3417d49..05038b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
@@ -22,6 +22,8 @@
 import android.graphics.drawable.RippleDrawable
 import android.os.UserManager
 import android.util.AttributeSet
+import android.util.Log
+import android.view.MotionEvent
 import android.view.View
 import android.widget.ImageView
 import android.widget.LinearLayout
@@ -101,4 +103,18 @@
         }
         multiUserAvatar.setImageDrawable(pictureToSet)
     }
-}
\ No newline at end of file
+
+    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
+        if (VERBOSE) Log.d(TAG, "FooterActionsView onInterceptTouchEvent ${ev?.string}")
+        return super.onInterceptTouchEvent(ev)
+    }
+
+    override fun onTouchEvent(event: MotionEvent?): Boolean {
+        if (VERBOSE) Log.d(TAG, "FooterActionsView onTouchEvent ${event?.string}")
+        return super.onTouchEvent(event)
+    }
+}
+private const val TAG = "FooterActionsView"
+private val VERBOSE = Log.isLoggable(TAG, Log.VERBOSE)
+private val MotionEvent.string
+    get() = "($id): ($x,$y)"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
index f6a55e6..478f7aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -48,8 +48,7 @@
     fun assertLegacyPipelineEnabled(): Unit =
         check(!isNewPipelineEnabled()) { "Old pipeline code running w/ new pipeline enabled" }
 
-    fun isNewPipelineEnabled(): Boolean =
-        featureFlags.isEnabled(Flags.NEW_NOTIFICATION_PIPELINE_RENDERING)
+    fun isNewPipelineEnabled(): Boolean = true
 
     fun isDevLoggingEnabled(): Boolean =
         featureFlags.isEnabled(Flags.NOTIFICATION_PIPELINE_DEVELOPER_LOGGING)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
new file mode 100644
index 0000000..63c37e9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
@@ -0,0 +1,12 @@
+set noparent
+
+# Bug component: 78010
+
+aaliomer@google.com
+aroederer@google.com
+jeffdq@google.com
+juliacr@google.com
+juliatuttle@google.com
+lynhan@google.com
+steell@google.com
+yurilin@google.com
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
index e98ae8d..792ff8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
@@ -115,6 +115,7 @@
     ) {
         sb.append(indent)
                 .append("[").append(index).append("] ")
+                .append(index.length() == 1 ? " " : "")
                 .append(entry.getKey());
 
         if (includeParent) {
@@ -192,7 +193,7 @@
             if (notifEntry.getAttachState().getSuppressedChanges().getSection() != null) {
                 rksb.append("suppressedSection=")
                         .append(notifEntry.getAttachState().getSuppressedChanges()
-                                .getSection())
+                                .getSection().getLabel())
                         .append(" ");
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 410593a..ecce1ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -104,6 +104,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -829,11 +830,9 @@
     @Override
     public void dump(PrintWriter pw, @NonNull String[] args) {
         final List<NotificationEntry> entries = new ArrayList<>(getAllNotifs());
+        entries.sort(Comparator.comparing(NotificationEntry::getKey));
 
-        pw.println("\t" + TAG + " unsorted/unfiltered notifications:");
-        if (entries.size() == 0) {
-            pw.println("\t\t None");
-        }
+        pw.println("\t" + TAG + " unsorted/unfiltered notifications: " + entries.size());
         pw.println(
                 ListDumper.dumpList(
                         entries,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index 6c99e3a..1d18ca3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -20,16 +20,14 @@
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogLevel.DEBUG
 import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.dagger.NotificationHeadsUpLog
-import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.log.dagger.NotificationInterruptLog
 import javax.inject.Inject
 
 class NotificationInterruptLogger @Inject constructor(
-    @NotificationLog val notifBuffer: LogBuffer,
-    @NotificationHeadsUpLog val hunBuffer: LogBuffer
+    @NotificationInterruptLog val buffer: LogBuffer
 ) {
     fun logHeadsUpFeatureChanged(useHeadsUp: Boolean) {
-        hunBuffer.log(TAG, INFO, {
+        buffer.log(TAG, INFO, {
             bool1 = useHeadsUp
         }, {
             "heads up is enabled=$bool1"
@@ -37,14 +35,14 @@
     }
 
     fun logWillDismissAll() {
-        hunBuffer.log(TAG, INFO, {
+        buffer.log(TAG, INFO, {
         }, {
             "dismissing any existing heads up notification on disable event"
         })
     }
 
     fun logNoBubbleNotAllowed(sbn: StatusBarNotification) {
-        notifBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No bubble up: not allowed to bubble: $str1"
@@ -52,7 +50,7 @@
     }
 
     fun logNoBubbleNoMetadata(sbn: StatusBarNotification) {
-        notifBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No bubble up: notification: $str1 doesn't have valid metadata"
@@ -60,14 +58,14 @@
     }
 
     fun logNoHeadsUpFeatureDisabled() {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
         }, {
             "No heads up: no huns"
         })
     }
 
     fun logNoHeadsUpPackageSnoozed(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No alerting: snoozed package: $str1"
@@ -75,7 +73,7 @@
     }
 
     fun logNoHeadsUpAlreadyBubbled(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No heads up: in unlocked shade where notification is shown as a bubble: $str1"
@@ -83,7 +81,7 @@
     }
 
     fun logNoHeadsUpSuppressedByDnd(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No heads up: suppressed by DND: $str1"
@@ -91,7 +89,7 @@
     }
 
     fun logNoHeadsUpNotImportant(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No heads up: unimportant notification: $str1"
@@ -99,7 +97,7 @@
     }
 
     fun logNoHeadsUpNotInUse(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No heads up: not in use: $str1"
@@ -110,7 +108,7 @@
         sbn: StatusBarNotification,
         suppressor: NotificationInterruptSuppressor
     ) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
             str2 = suppressor.name
         }, {
@@ -119,7 +117,7 @@
     }
 
     fun logHeadsUp(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "Heads up: $str1"
@@ -127,7 +125,7 @@
     }
 
     fun logNoAlertingFilteredOut(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No alerting: filtered notification: $str1"
@@ -135,7 +133,7 @@
     }
 
     fun logNoAlertingGroupAlertBehavior(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No alerting: suppressed due to group alert behavior: $str1"
@@ -147,7 +145,7 @@
         suppressor: NotificationInterruptSuppressor,
         awake: Boolean
     ) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
             str2 = suppressor.name
             bool1 = awake
@@ -157,7 +155,7 @@
     }
 
     fun logNoAlertingRecentFullscreen(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No alerting: recent fullscreen: $str1"
@@ -165,7 +163,7 @@
     }
 
     fun logNoPulsingSettingDisabled(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No pulsing: disabled by setting: $str1"
@@ -173,7 +171,7 @@
     }
 
     fun logNoPulsingBatteryDisabled(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No pulsing: disabled by battery saver: $str1"
@@ -181,7 +179,7 @@
     }
 
     fun logNoPulsingNoAlert(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No pulsing: notification shouldn't alert: $str1"
@@ -189,7 +187,7 @@
     }
 
     fun logNoPulsingNoAmbientEffect(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No pulsing: ambient effect suppressed: $str1"
@@ -197,7 +195,7 @@
     }
 
     fun logNoPulsingNotImportant(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "No pulsing: not important enough: $str1"
@@ -205,7 +203,7 @@
     }
 
     fun logPulsing(sbn: StatusBarNotification) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = sbn.key
         }, {
             "Pulsing: $str1"
@@ -213,7 +211,7 @@
     }
 
     fun keyguardHideNotification(key: String) {
-        hunBuffer.log(TAG, DEBUG, {
+        buffer.log(TAG, DEBUG, {
             str1 = key
         }, {
             "Keyguard Hide Notification: $str1"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index b785435..cd4a44e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -141,7 +141,7 @@
     public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
     private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
 
-    private boolean mUpdateBackgroundOnUpdate;
+    private boolean mUpdateSelfBackgroundOnUpdate;
     private boolean mNotificationTranslationFinished = false;
     private boolean mIsSnoozed;
     private boolean mIsFaded;
@@ -553,9 +553,28 @@
         updateLimits();
         updateShelfIconColor();
         updateRippleAllowed();
-        if (mUpdateBackgroundOnUpdate) {
-            mUpdateBackgroundOnUpdate = false;
-            updateBackgroundColors();
+        if (mUpdateSelfBackgroundOnUpdate) {
+            // Because this is triggered by UiMode change which we already propagated to children,
+            // we know that child rows will receive the same event, and will update their own
+            // backgrounds when they finish inflating, so propagating again would be redundant.
+            mUpdateSelfBackgroundOnUpdate = false;
+            updateBackgroundColorsOfSelf();
+        }
+    }
+
+    private void updateBackgroundColorsOfSelf() {
+        super.updateBackgroundColors();
+    }
+
+    @Override
+    public void updateBackgroundColors() {
+        // Because this call is made by the NSSL only on attached rows at the moment of the
+        // UiMode or Theme change, we have to propagate to our child views.
+        updateBackgroundColorsOfSelf();
+        if (mIsSummaryWithChildren) {
+            for (ExpandableNotificationRow child : mChildrenContainer.getAttachedChildren()) {
+                child.updateBackgroundColors();
+            }
         }
     }
 
@@ -1242,7 +1261,7 @@
     }
 
     public void onUiModeChanged() {
-        mUpdateBackgroundOnUpdate = true;
+        mUpdateSelfBackgroundOnUpdate = true;
         reInflateViews();
         if (mChildrenContainer != null) {
             for (ExpandableNotificationRow child : mChildrenContainer.getAttachedChildren()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index e042e9c..8f73b80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -186,10 +186,12 @@
      * @param notifyListeners Whether the listener should be informed about the change.
      */
     public void setActualHeight(int actualHeight, boolean notifyListeners) {
-        mActualHeight = actualHeight;
-        updateClipping();
-        if (notifyListeners) {
-            notifyHeightChanged(false  /* needsAnimation */);
+        if (mActualHeight != actualHeight) {
+            mActualHeight = actualHeight;
+            updateClipping();
+            if (notifyListeners) {
+                notifyHeightChanged(false  /* needsAnimation */);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 8c61764..5181af7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1597,7 +1597,7 @@
                         }
                     }
                 });
-        mStatusBarKeyguardViewManager.registerCentralSurfaces(
+        mKeyguardViewMediator.registerCentralSurfaces(
                 /* statusBar= */ this,
                 mNotificationPanelViewController,
                 mPanelExpansionStateManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 33e54a7..6009eba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -4194,6 +4194,11 @@
 
             @Override
             public boolean onInterceptTouchEvent(MotionEvent event) {
+                if (SPEW_LOGCAT) {
+                    Log.v(TAG,
+                            "NPVC onInterceptTouchEvent (" + event.getId() + "): (" + event.getX()
+                                    + "," + event.getY() + ")");
+                }
                 if (mBlockTouches || mQs.disallowPanelTouches()) {
                     return false;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS
new file mode 100644
index 0000000..18f0fb3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS
@@ -0,0 +1,3 @@
+per-file *Notification* = set noparent
+per-file *Notification* = file:../notification/OWNERS
+per-file NotificationIcon* = ccassidy@google.com, evanlaird@google.com, pixel@google.com
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
index 313d56f..4e9030f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
@@ -16,17 +16,17 @@
 
 package com.android.systemui.util.condition;
 
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.policy.CallbackController;
 
 import org.jetbrains.annotations.NotNull;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.stream.Collectors;
@@ -37,149 +37,120 @@
  * {@link Monitor} takes in a set of conditions, monitors whether all of them have
  * been fulfilled, and informs any registered listeners.
  */
-public class Monitor implements CallbackController<Monitor.Callback> {
+public class Monitor {
     private final String mTag = getClass().getSimpleName();
-
-    private final ArrayList<Callback> mCallbacks = new ArrayList<>();
-
-    // Set of all conditions that need to be monitored.
-    private final Set<Condition> mConditions;
     private final Executor mExecutor;
 
-    // Whether all conditions have been met.
-    private boolean mAllConditionsMet = false;
+    private final HashMap<Condition, ArraySet<Subscription.Token>> mConditions = new HashMap<>();
+    private final HashMap<Subscription.Token, SubscriptionState> mSubscriptions = new HashMap<>();
 
-    // Whether the monitor has started listening for all the conditions.
-    private boolean mHaveConditionsStarted = false;
+    private static class SubscriptionState {
+        private final Subscription mSubscription;
+        private Boolean mAllConditionsMet;
+
+        SubscriptionState(Subscription subscription) {
+            mSubscription = subscription;
+        }
+
+        public Set<Condition> getConditions() {
+            return mSubscription.mConditions;
+        }
+
+        public void update() {
+            // Overriding conditions do not override each other
+            final Collection<Condition> overridingConditions = mSubscription.mConditions.stream()
+                    .filter(Condition::isOverridingCondition).collect(Collectors.toSet());
+
+            final Collection<Condition> targetCollection = overridingConditions.isEmpty()
+                    ? mSubscription.mConditions : overridingConditions;
+
+            final boolean newAllConditionsMet = targetCollection.isEmpty() ? true : targetCollection
+                    .stream()
+                    .map(Condition::isConditionMet)
+                    .allMatch(conditionMet -> conditionMet);
+
+            if (mAllConditionsMet != null && newAllConditionsMet == mAllConditionsMet) {
+                return;
+            }
+
+            mAllConditionsMet = newAllConditionsMet;
+            mSubscription.mCallback.onConditionsChanged(mAllConditionsMet);
+        }
+    }
 
     // Callback for when each condition has been updated.
     private final Condition.Callback mConditionCallback = new Condition.Callback() {
         @Override
         public void onConditionChanged(Condition condition) {
-            mExecutor.execute(() -> updateConditionMetState());
+            mExecutor.execute(() -> updateConditionMetState(condition));
         }
     };
 
     @Inject
-    public Monitor(@Main Executor executor, Set<Condition> conditions) {
-        mConditions = new HashSet<>();
+    public Monitor(@Main Executor executor) {
         mExecutor = executor;
-
-        if (conditions != null) {
-            mConditions.addAll(conditions);
-        }
     }
 
-    private void updateConditionMetState() {
-        // Overriding conditions do not override each other
-        final Collection<Condition> overridingConditions = mConditions.stream()
-                .filter(Condition::isOverridingCondition).collect(Collectors.toSet());
-
-        final Collection<Condition> targetCollection = overridingConditions.isEmpty()
-                ? mConditions : overridingConditions;
-
-        final boolean newAllConditionsMet = targetCollection.isEmpty() ? true : targetCollection
-                .stream()
-                .map(Condition::isConditionMet)
-                .allMatch(conditionMet -> conditionMet);
-
-        if (newAllConditionsMet == mAllConditionsMet) {
-            return;
-        }
-
-        if (shouldLog()) Log.d(mTag, "all conditions met: " + newAllConditionsMet);
-        mAllConditionsMet = newAllConditionsMet;
-
-        // Updates all callbacks.
-        final Iterator<Callback> iterator = mCallbacks.iterator();
-        while (iterator.hasNext()) {
-            final Callback callback = iterator.next();
-            if (callback == null) {
-                iterator.remove();
-            } else {
-                callback.onConditionsChanged(mAllConditionsMet);
-            }
-        }
-    }
-
-    private void addConditionLocked(@NotNull Condition condition) {
-        mConditions.add(condition);
-
-        if (!mHaveConditionsStarted) {
-            return;
-        }
-
-        condition.addCallback(mConditionCallback);
-        updateConditionMetState();
+    private void updateConditionMetState(Condition condition) {
+        mConditions.get(condition).stream().forEach(token -> mSubscriptions.get(token).update());
     }
 
     /**
-     * Adds a condition for the monitor to listen to and consider when determining whether the
-     * overall condition state is met.
+     * Registers a callback and the set of conditions to trigger it.
+     * @param subscription A {@link Subscription} detailing the desired conditions and callback.
+     * @return A {@link Subscription.Token} that can be used to remove the subscription.
      */
-    public void addCondition(@NotNull Condition condition) {
-        mExecutor.execute(() -> addConditionLocked(condition));
-    }
+    public Subscription.Token addSubscription(@NotNull Subscription subscription) {
+        final Subscription.Token token = new Subscription.Token();
+        final SubscriptionState state = new SubscriptionState(subscription);
 
-    /**
-     * Removes a condition from further consideration.
-     */
-    public void removeCondition(@NotNull Condition condition) {
         mExecutor.execute(() -> {
-            mConditions.remove(condition);
+            mSubscriptions.put(token, state);
 
-            if (!mHaveConditionsStarted) {
+            // Add and associate conditions.
+            subscription.getConditions().stream().forEach(condition -> {
+                if (!mConditions.containsKey(condition)) {
+                    mConditions.put(condition, new ArraySet<>());
+                    condition.addCallback(mConditionCallback);
+                }
+
+                mConditions.get(condition).add(token);
+            });
+
+            // Update subscription state.
+            state.update();
+
+        });
+        return token;
+    }
+
+    /**
+     * Removes a subscription from participating in future callbacks.
+     * @param token The {@link Subscription.Token} returned when the {@link Subscription} was
+     *              originally added.
+     */
+    public void removeSubscription(@NotNull Subscription.Token token) {
+        mExecutor.execute(() -> {
+            if (shouldLog()) Log.d(mTag, "removing callback");
+            if (!mSubscriptions.containsKey(token)) {
+                Log.e(mTag, "subscription not present:" + token);
                 return;
             }
 
-            condition.removeCallback(mConditionCallback);
-            updateConditionMetState();
-        });
-    }
+            mSubscriptions.remove(token).getConditions().forEach(condition -> {
+                if (!mConditions.containsKey(condition)) {
+                    Log.e(mTag, "condition not present:" + condition);
+                    return;
 
-    private void addCallbackLocked(@NotNull Callback callback) {
-        if (mCallbacks.contains(callback)) {
-            return;
-        }
-
-        if (shouldLog()) Log.d(mTag, "adding callback");
-        mCallbacks.add(callback);
-
-        // Updates the callback immediately.
-        callback.onConditionsChanged(mAllConditionsMet);
-
-        if (!mHaveConditionsStarted) {
-            if (shouldLog()) Log.d(mTag, "starting all conditions");
-            mConditions.forEach(condition -> condition.addCallback(mConditionCallback));
-            updateConditionMetState();
-            mHaveConditionsStarted = true;
-        }
-    }
-
-    @Override
-    public void addCallback(@NotNull Callback callback) {
-        mExecutor.execute(() -> addCallbackLocked(callback));
-    }
-
-    @Override
-    public void removeCallback(@NotNull Callback callback) {
-        mExecutor.execute(() -> {
-            if (shouldLog()) Log.d(mTag, "removing callback");
-            final Iterator<Callback> iterator = mCallbacks.iterator();
-            while (iterator.hasNext()) {
-                final Callback cb = iterator.next();
-                if (cb == null || cb == callback) {
-                    iterator.remove();
                 }
-            }
+                final Set<Subscription.Token> conditionSubscriptions = mConditions.get(condition);
 
-            if (mCallbacks.isEmpty() && mHaveConditionsStarted) {
-                if (shouldLog()) Log.d(mTag, "stopping all conditions");
-                mConditions.forEach(condition -> condition.removeCallback(mConditionCallback));
-
-                mAllConditionsMet = false;
-                mHaveConditionsStarted = false;
-            }
+                conditionSubscriptions.remove(token);
+                if (conditionSubscriptions.isEmpty()) {
+                    condition.removeCallback(mConditionCallback);
+                    mConditions.remove(condition);
+                }
+            });
         });
     }
 
@@ -188,10 +159,92 @@
     }
 
     /**
+     * A {@link Subscription} represents a set of conditions and a callback that is informed when
+     * these conditions change.
+     */
+    public static class Subscription {
+        private final Set<Condition> mConditions;
+        private final Callback mCallback;
+
+        /** */
+        public Subscription(Set<Condition> conditions, Callback callback) {
+            this.mConditions = Collections.unmodifiableSet(conditions);
+            this.mCallback = callback;
+        }
+
+        public Set<Condition> getConditions() {
+            return mConditions;
+        }
+
+        public Callback getCallback() {
+            return mCallback;
+        }
+
+        /**
+         * A {@link Token} is an identifier that is associated with a {@link Subscription} which is
+         * registered with a {@link Monitor}.
+         */
+        public static class Token {
+        }
+
+        /**
+         * {@link Builder} is a helper class for constructing a {@link Subscription}.
+         */
+        public static class Builder {
+            private final Callback mCallback;
+            private final ArraySet<Condition> mConditions;
+
+            /**
+             * Default constructor specifying the {@link Callback} for the {@link Subscription}.
+             * @param callback
+             */
+            public Builder(Callback callback) {
+                mCallback = callback;
+                mConditions = new ArraySet<>();
+            }
+
+            /**
+             * Adds a {@link Condition} to be associated with the {@link Subscription}.
+             * @param condition
+             * @return The updated {@link Builder}.
+             */
+            public Builder addCondition(Condition condition) {
+                mConditions.add(condition);
+                return this;
+            }
+
+            /**
+             * Adds a set of {@link Condition} to be associated with the {@link Subscription}.
+             * @param condition
+             * @return The updated {@link Builder}.
+             */
+            public Builder addConditions(Set<Condition> condition) {
+                mConditions.addAll(condition);
+                return this;
+            }
+
+            /**
+             * Builds the {@link Subscription}.
+             * @return The resulting {@link Subscription}.
+             */
+            public Subscription build() {
+                return new Subscription(mConditions, mCallback);
+            }
+        }
+    }
+
+    /**
      * Callback that receives updates of whether all conditions have been fulfilled.
      */
     public interface Callback {
         /**
+         * Returns the conditions associated with this callback.
+         */
+        default ArrayList<Condition> getConditions() {
+            return new ArrayList<>();
+        }
+
+        /**
          * Triggered when the fulfillment of all conditions have been met.
          *
          * @param allConditionsMet True if all conditions have been fulfilled. False if none or
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java
deleted file mode 100644
index 8e739d6..0000000
--- a/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.util.condition.dagger;
-
-import com.android.systemui.util.condition.Condition;
-import com.android.systemui.util.condition.Monitor;
-
-import java.util.Set;
-
-import dagger.BindsInstance;
-import dagger.Subcomponent;
-
-/**
- * Component for {@link Monitor}.
- */
-@Subcomponent
-public interface MonitorComponent {
-    /**
-     * Factory for {@link MonitorComponent}.
-     */
-    @Subcomponent.Factory
-    interface Factory {
-        MonitorComponent create(@BindsInstance Set<Condition> conditions);
-    }
-
-    /**
-     * Provides {@link Monitor}.
-     * @return
-     */
-    Monitor getMonitor();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
index 7892d6e..981bf01 100644
--- a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
@@ -18,7 +18,6 @@
 
 import com.android.systemui.util.RingerModeTracker;
 import com.android.systemui.util.RingerModeTrackerImpl;
-import com.android.systemui.util.condition.dagger.MonitorComponent;
 import com.android.systemui.util.wrapper.UtilWrapperModule;
 
 import dagger.Binds;
@@ -27,9 +26,6 @@
 /** Dagger Module for code in the util package. */
 @Module(includes = {
                 UtilWrapperModule.class
-        },
-        subcomponents = {
-                MonitorComponent.class,
         })
 public interface UtilModule {
     /** */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 0a1e45c..9ffc5a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -461,7 +461,8 @@
                     /* reportsTouchCoordinate*/ false,
                     /* requiresTouchscreen */ false,
                     /* ignoresSetting */ false,
-                    requiresTouchScreen);
+                    requiresTouchScreen,
+                    /* immediatelyReRegister */ true);
         }
 
         public TriggerSensor createDozeSensor(
@@ -477,7 +478,8 @@
                     /* reportsTouchCoordinate*/ false,
                     /* requiresTouchscreen */ false,
                     /* ignoresSetting */ false,
-                    /* requiresTouchScreen */false);
+                    /* requiresTouchScreen */ false,
+                    /* immediatelyReRegister*/ true);
         }
 
         /**
@@ -492,7 +494,8 @@
                     /* reportsTouchCoordinate*/ false,
                     /* requiresTouchscreen */ false,
                     /* ignoresSetting */ true,
-                    /* requiresProx */false,
+                    /* requiresProx */ false,
+                    /* immediatelyReRegister */ true,
                     posture);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 3c28d48..d334694 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -57,6 +57,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Optional;
 import java.util.concurrent.Executor;
 
 @SmallTest
@@ -115,7 +116,7 @@
                 mNextAlarmController,
                 mDateFormatUtil,
                 mSensorPrivacyController,
-                mDreamOverlayNotificationCountProvider,
+                Optional.of(mDreamOverlayNotificationCountProvider),
                 mZenModeController,
                 mStatusBarWindowStateController);
     }
@@ -231,6 +232,26 @@
     }
 
     @Test
+    public void testNotificationsIconNotShownWhenCountProviderAbsent() {
+        DreamOverlayStatusBarViewController controller = new DreamOverlayStatusBarViewController(
+                mView,
+                mResources,
+                mMainExecutor,
+                mConnectivityManager,
+                mTouchSession,
+                mAlarmManager,
+                mNextAlarmController,
+                mDateFormatUtil,
+                mSensorPrivacyController,
+                Optional.empty(),
+                mZenModeController,
+                mStatusBarWindowStateController);
+        controller.onViewAttached();
+        verify(mView, never()).showIcon(
+                eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+    }
+
+    @Test
     public void testOnViewAttachedShowsPriorityModeIconWhenEnabled() {
         when(mZenModeController.getZen()).thenReturn(
                 Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java
index cefdf28..dc1ae0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.dreams;
 
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -23,10 +25,13 @@
 import android.app.smartspace.SmartspaceTarget;
 import android.content.Context;
 import android.testing.AndroidTestingRunner;
+import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.complication.Complication;
+import com.android.systemui.dreams.complication.ComplicationViewModel;
 import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
 import com.android.systemui.plugins.BcSmartspaceDataPlugin;
 
@@ -55,6 +60,12 @@
     @Mock
     private SmartSpaceComplication mComplication;
 
+    @Mock
+    private ComplicationViewModel mComplicationViewModel;
+
+    @Mock
+    private View mBcSmartspaceView;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
@@ -90,4 +101,13 @@
         listenerCaptor.getValue().onSmartspaceTargetsUpdated(Arrays.asList(target));
         verify(mDreamOverlayStateController).addComplication(eq(mComplication));
     }
+
+    @Test
+    public void testGetViewReusesSameView() {
+        final SmartSpaceComplication complication = new SmartSpaceComplication(getContext(),
+                mSmartspaceController);
+        final Complication.ViewHolder viewHolder = complication.createView(mComplicationViewModel);
+        when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mBcSmartspaceView);
+        assertEquals(viewHolder.getView(), viewHolder.getView());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/AirQualityColorPickerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/AirQualityColorPickerTest.java
new file mode 100644
index 0000000..33be5dc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/AirQualityColorPickerTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.complication;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class AirQualityColorPickerTest extends SysuiTestCase {
+    private static final int DEFAULT_COLOR = 0;
+    private static final int MOCK_COLOR_1 = 1;
+    private static final int MOCK_COLOR_2 = 2;
+    private static final int MOCK_COLOR_3 = 3;
+    private static final int MOCK_COLOR_4 = 4;
+    private static final int MOCK_COLOR_5 = 5;
+
+    private static final int[] MOCK_THRESHOLDS = {-1, 100, 200, 201, 500};
+    private static final int[] MOCK_COLORS =
+            {MOCK_COLOR_1, MOCK_COLOR_2, MOCK_COLOR_3, MOCK_COLOR_4, MOCK_COLOR_5};
+    private static final int[] EMPTY_ARRAY = {};
+
+    @Test
+    public void testEmptyThresholds() {
+        final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
+                EMPTY_ARRAY,
+                MOCK_COLORS,
+                DEFAULT_COLOR);
+        assertThat(colorPicker.getColorForValue("110 AQI")).isEqualTo(DEFAULT_COLOR);
+    }
+
+    @Test
+    public void testEmptyColors() {
+        final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
+                MOCK_THRESHOLDS,
+                EMPTY_ARRAY,
+                DEFAULT_COLOR);
+        assertThat(colorPicker.getColorForValue("110 AQI")).isEqualTo(DEFAULT_COLOR);
+    }
+
+    @Test
+    public void testEmptyAqiString() {
+        final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
+                MOCK_THRESHOLDS,
+                MOCK_COLORS,
+                DEFAULT_COLOR);
+        assertThat(colorPicker.getColorForValue("")).isEqualTo(DEFAULT_COLOR);
+    }
+
+    @Test
+    public void testInvalidAqiString() {
+        final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
+                MOCK_THRESHOLDS,
+                MOCK_COLORS,
+                DEFAULT_COLOR);
+        assertThat(colorPicker.getColorForValue("invalid")).isEqualTo(DEFAULT_COLOR);
+    }
+
+    @Test
+    public void testZeroAirQuality() {
+        final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
+                MOCK_THRESHOLDS,
+                MOCK_COLORS,
+                DEFAULT_COLOR);
+        assertThat(colorPicker.getColorForValue("0 AQI")).isEqualTo(MOCK_COLOR_1);
+    }
+
+    @Test
+    public void testVeryLargeAirQuality() {
+        final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
+                MOCK_THRESHOLDS,
+                MOCK_COLORS,
+                DEFAULT_COLOR);
+        assertThat(colorPicker.getColorForValue("100000 AQI")).isEqualTo(MOCK_COLOR_5);
+    }
+
+    @Test
+    public void testAirQuality200() {
+        final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
+                MOCK_THRESHOLDS,
+                MOCK_COLORS,
+                DEFAULT_COLOR);
+        assertThat(colorPicker.getColorForValue("200 AQI")).isEqualTo(MOCK_COLOR_2);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamAirQualityComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamAirQualityComplicationTest.java
new file mode 100644
index 0000000..b8a7059
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamAirQualityComplicationTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.systemui.dreams.complication;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.widget.TextView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.complication.DreamAirQualityComplication.DreamAirQualityViewController;
+import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
+import com.android.systemui.plugins.ActivityStarter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamAirQualityComplicationTest extends SysuiTestCase {
+    private static final String TRAMPOLINE_COMPONENT = "TestComponent";
+
+    @Mock
+    private DreamSmartspaceController mDreamSmartspaceController;
+
+    @Mock
+    private DreamOverlayStateController mDreamOverlayStateController;
+
+    @Mock
+    private DreamAirQualityComplication mComplication;
+
+    @Mock
+    private AirQualityColorPicker mColorPicker;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    /**
+     * Ensures {@link DreamAirQualityComplication} is registered.
+     */
+    @Test
+    public void testComplicationRegistered() {
+        final DreamAirQualityComplication.Registrant registrant =
+                new DreamAirQualityComplication.Registrant(
+                        mContext,
+                        mDreamOverlayStateController,
+                        mComplication);
+        registrant.start();
+        verify(mDreamOverlayStateController).addComplication(eq(mComplication));
+    }
+
+    @Test
+    public void testGetUnfilteredTargets() {
+        final DreamAirQualityViewController controller =
+                new DreamAirQualityViewController(
+                        mock(TextView.class),
+                        mDreamSmartspaceController,
+                        TRAMPOLINE_COMPONENT,
+                        mock(ActivityStarter.class),
+                        mColorPicker);
+        controller.onViewAttached();
+        verify(mDreamSmartspaceController).addUnfilteredListener(any());
+        controller.onViewDetached();
+        verify(mDreamSmartspaceController).removeUnfilteredListener(any());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index 11fe87d..ee10426 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -56,7 +56,9 @@
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.junit.MockitoJUnit
 import org.mockito.Mockito.`when` as whenever
 
@@ -64,6 +66,7 @@
 private const val KEY_OLD = "TEST_KEY_OLD"
 private const val PACKAGE = "PKG"
 private const val SESSION_KEY = "SESSION_KEY"
+private const val DEVICE_ID = "DEVICE_ID"
 private const val DEVICE_NAME = "DEVICE_NAME"
 private const val REMOTE_DEVICE_NAME = "REMOTE_DEVICE_NAME"
 private const val BROADCAST_APP_NAME = "BROADCAST_APP_NAME"
@@ -478,6 +481,89 @@
     }
 
     @Test
+    fun deviceIdChanged_informListener() {
+        // GIVEN a notification is added, with a particular device connected
+        whenever(device.id).thenReturn(DEVICE_ID)
+        manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+
+        // and later the manager gets a new device ID
+        val deviceCallback = captureCallback()
+        val updatedId = DEVICE_ID + "_new"
+        whenever(device.id).thenReturn(updatedId)
+        deviceCallback.onDeviceListUpdate(mutableListOf(device))
+
+        // THEN the listener gets the updated info
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+
+        val dataCaptor = ArgumentCaptor.forClass(MediaDeviceData::class.java)
+        verify(listener, times(2)).onMediaDeviceChanged(eq(KEY), any(), dataCaptor.capture())
+
+        val firstDevice = dataCaptor.allValues.get(0)
+        assertThat(firstDevice.id).isEqualTo(DEVICE_ID)
+
+        val secondDevice = dataCaptor.allValues.get(1)
+        assertThat(secondDevice.id).isEqualTo(updatedId)
+    }
+
+    @Test
+    fun deviceNameChanged_informListener() {
+        // GIVEN a notification is added, with a particular device connected
+        whenever(device.id).thenReturn(DEVICE_ID)
+        whenever(device.name).thenReturn(DEVICE_NAME)
+        manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+
+        // and later the manager gets a new device name
+        val deviceCallback = captureCallback()
+        val updatedName = DEVICE_NAME + "_new"
+        whenever(device.name).thenReturn(updatedName)
+        deviceCallback.onDeviceListUpdate(mutableListOf(device))
+
+        // THEN the listener gets the updated info
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+
+        val dataCaptor = ArgumentCaptor.forClass(MediaDeviceData::class.java)
+        verify(listener, times(2)).onMediaDeviceChanged(eq(KEY), any(), dataCaptor.capture())
+
+        val firstDevice = dataCaptor.allValues.get(0)
+        assertThat(firstDevice.name).isEqualTo(DEVICE_NAME)
+
+        val secondDevice = dataCaptor.allValues.get(1)
+        assertThat(secondDevice.name).isEqualTo(updatedName)
+    }
+
+    @Test
+    fun deviceIconChanged_doesNotCallListener() {
+        // GIVEN a notification is added, with a particular device connected
+        whenever(device.id).thenReturn(DEVICE_ID)
+        whenever(device.name).thenReturn(DEVICE_NAME)
+        val firstIcon = mock(Drawable::class.java)
+        whenever(device.icon).thenReturn(firstIcon)
+        manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+
+        val dataCaptor = ArgumentCaptor.forClass(MediaDeviceData::class.java)
+        verify(listener).onMediaDeviceChanged(eq(KEY), any(), dataCaptor.capture())
+
+        // and later the manager gets a callback with only the icon changed
+        val deviceCallback = captureCallback()
+        val secondIcon = mock(Drawable::class.java)
+        whenever(device.icon).thenReturn(secondIcon)
+        deviceCallback.onDeviceListUpdate(mutableListOf(device))
+
+        // THEN the listener is not called again
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+        verifyNoMoreInteractions(listener)
+    }
+
+    @Test
     fun testRemotePlaybackDeviceOverride() {
         whenever(route.name).thenReturn(DEVICE_NAME)
         val deviceData = MediaDeviceData(false, null, REMOTE_DEVICE_NAME, null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index b1bf971..f8b39e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -43,6 +43,7 @@
 
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.graphics.Color;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
@@ -110,6 +111,28 @@
     }
 
     @Test
+    public void testUpdateBackgroundColors_isRecursive() {
+        mGroupRow.setTintColor(Color.RED);
+        mGroupRow.getChildNotificationAt(0).setTintColor(Color.GREEN);
+        mGroupRow.getChildNotificationAt(1).setTintColor(Color.BLUE);
+
+        assertThat(mGroupRow.getCurrentBackgroundTint()).isEqualTo(Color.RED);
+        assertThat(mGroupRow.getChildNotificationAt(0).getCurrentBackgroundTint())
+                .isEqualTo(Color.GREEN);
+        assertThat(mGroupRow.getChildNotificationAt(1).getCurrentBackgroundTint())
+                .isEqualTo(Color.BLUE);
+
+        mGroupRow.updateBackgroundColors();
+
+        int resetTint = mGroupRow.getCurrentBackgroundTint();
+        assertThat(resetTint).isNotEqualTo(Color.RED);
+        assertThat(mGroupRow.getChildNotificationAt(0).getCurrentBackgroundTint())
+                .isEqualTo(resetTint);
+        assertThat(mGroupRow.getChildNotificationAt(1).getCurrentBackgroundTint())
+                .isEqualTo(resetTint);
+    }
+
+    @Test
     public void testSetSensitiveOnNotifRowNotifiesOfHeightChange() throws InterruptedException {
         // GIVEN a sensitive notification row that's currently redacted
         measureAndLayout(mNotifRow);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
index 7589616..1e35b0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
@@ -65,7 +65,12 @@
         mCondition3 = spy(new FakeCondition());
         mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3));
 
-        mConditionMonitor = new Monitor(mExecutor, mConditions);
+        mConditionMonitor = new Monitor(mExecutor);
+    }
+
+    public Monitor.Subscription.Builder getDefaultBuilder(Monitor.Callback callback) {
+        return new Monitor.Subscription.Builder(callback)
+                .addConditions(mConditions);
     }
 
     @Test
@@ -74,11 +79,19 @@
         final Condition regularCondition = Mockito.mock(Condition.class);
         final Monitor.Callback callback = Mockito.mock(Monitor.Callback.class);
 
-        final Monitor monitor = new Monitor(
-                mExecutor,
-                new HashSet<>(Arrays.asList(overridingCondition, regularCondition)));
+        final Monitor.Callback referenceCallback = Mockito.mock(Monitor.Callback.class);
 
-        monitor.addCallback(callback);
+        final Monitor monitor = new Monitor(mExecutor);
+
+        monitor.addSubscription(getDefaultBuilder(callback)
+                .addCondition(overridingCondition)
+                .addCondition(regularCondition)
+                .build());
+
+        monitor.addSubscription(getDefaultBuilder(referenceCallback)
+                .addCondition(regularCondition)
+                .build());
+
         mExecutor.runAllReady();
 
         when(overridingCondition.isOverridingCondition()).thenReturn(true);
@@ -94,7 +107,9 @@
         mExecutor.runAllReady();
 
         verify(callback).onConditionsChanged(eq(true));
+        verify(referenceCallback).onConditionsChanged(eq(false));
         Mockito.clearInvocations(callback);
+        Mockito.clearInvocations(referenceCallback);
 
         when(regularCondition.isConditionMet()).thenReturn(true);
         when(overridingCondition.isConditionMet()).thenReturn(false);
@@ -103,12 +118,7 @@
         mExecutor.runAllReady();
 
         verify(callback).onConditionsChanged(eq(false));
-
-        clearInvocations(callback);
-        monitor.removeCondition(overridingCondition);
-        mExecutor.runAllReady();
-
-        verify(callback).onConditionsChanged(eq(true));
+        verify(referenceCallback, never()).onConditionsChanged(anyBoolean());
     }
 
     /**
@@ -122,11 +132,13 @@
         final Condition regularCondition = Mockito.mock(Condition.class);
         final Monitor.Callback callback = Mockito.mock(Monitor.Callback.class);
 
-        final Monitor monitor = new Monitor(
-                mExecutor,
-                new HashSet<>(Arrays.asList(overridingCondition, overridingCondition2,
-                        regularCondition)));
-        monitor.addCallback(callback);
+        final Monitor monitor = new Monitor(mExecutor);
+
+        monitor.addSubscription(getDefaultBuilder(callback)
+                .addCondition(overridingCondition)
+                .addCondition(overridingCondition2)
+                .build());
+
         mExecutor.runAllReady();
 
         when(overridingCondition.isOverridingCondition()).thenReturn(true);
@@ -151,13 +163,13 @@
     public void addCallback_addFirstCallback_addCallbackToAllConditions() {
         final Monitor.Callback callback1 =
                 mock(Monitor.Callback.class);
-        mConditionMonitor.addCallback(callback1);
+        mConditionMonitor.addSubscription(getDefaultBuilder(callback1).build());
         mExecutor.runAllReady();
         mConditions.forEach(condition -> verify(condition).addCallback(any()));
 
         final Monitor.Callback callback2 =
                 mock(Monitor.Callback.class);
-        mConditionMonitor.addCallback(callback2);
+        mConditionMonitor.addSubscription(getDefaultBuilder(callback2).build());
         mExecutor.runAllReady();
         mConditions.forEach(condition -> verify(condition, times(1)).addCallback(any()));
     }
@@ -166,7 +178,7 @@
     public void addCallback_addFirstCallback_reportWithDefaultValue() {
         final Monitor.Callback callback =
                 mock(Monitor.Callback.class);
-        mConditionMonitor.addCallback(callback);
+        mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
         mExecutor.runAllReady();
         verify(callback).onConditionsChanged(false);
     }
@@ -177,66 +189,65 @@
                 mock(Monitor.Callback.class);
         final Condition condition = mock(Condition.class);
         when(condition.isConditionMet()).thenReturn(true);
-        final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)));
-        monitor.addCallback(callback1);
+        final Monitor monitor = new Monitor(mExecutor);
+        monitor.addSubscription(new Monitor.Subscription.Builder(callback1)
+                .addCondition(condition)
+                .build());
 
         final Monitor.Callback callback2 =
                 mock(Monitor.Callback.class);
-        monitor.addCallback(callback2);
+        monitor.addSubscription(new Monitor.Subscription.Builder(callback2)
+                .addCondition(condition)
+                .build());
         mExecutor.runAllReady();
         verify(callback2).onConditionsChanged(eq(true));
     }
 
     @Test
     public void addCallback_noConditions_reportAllConditionsMet() {
-        final Monitor monitor = new Monitor(mExecutor, new HashSet<>());
+        final Monitor monitor = new Monitor(mExecutor);
         final Monitor.Callback callback = mock(Monitor.Callback.class);
 
-        monitor.addCallback(callback);
+        monitor.addSubscription(new Monitor.Subscription.Builder(callback).build());
         mExecutor.runAllReady();
         verify(callback).onConditionsChanged(true);
     }
 
     @Test
-    public void addCallback_withMultipleInstancesOfTheSameCallback_registerOnlyOne() {
-        final Monitor monitor = new Monitor(mExecutor, new HashSet<>());
-        final Monitor.Callback callback = mock(Monitor.Callback.class);
-
-        // Adds the same instance multiple times.
-        monitor.addCallback(callback);
-        monitor.addCallback(callback);
-        monitor.addCallback(callback);
+    public void removeCallback_noFailureOnDoubleRemove() {
+        final Condition condition = mock(Condition.class);
+        final Monitor monitor = new Monitor(mExecutor);
+        final Monitor.Callback callback =
+                mock(Monitor.Callback.class);
+        final Monitor.Subscription.Token token = monitor.addSubscription(
+                new Monitor.Subscription.Builder(callback).addCondition(condition).build()
+        );
+        monitor.removeSubscription(token);
         mExecutor.runAllReady();
-
-        // Callback should only be triggered once.
-        verify(callback, times(1)).onConditionsChanged(true);
+        // Ensure second removal doesn't cause an exception.
+        monitor.removeSubscription(token);
+        mExecutor.runAllReady();
     }
 
     @Test
     public void removeCallback_shouldNoLongerReceiveUpdate() {
         final Condition condition = mock(Condition.class);
-        final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)));
+        final Monitor monitor = new Monitor(mExecutor);
         final Monitor.Callback callback =
                 mock(Monitor.Callback.class);
-        monitor.addCallback(callback);
-        monitor.removeCallback(callback);
+        final Monitor.Subscription.Token token = monitor.addSubscription(
+                new Monitor.Subscription.Builder(callback).addCondition(condition).build()
+        );
+        monitor.removeSubscription(token);
         mExecutor.runAllReady();
         clearInvocations(callback);
 
         final ArgumentCaptor<Condition.Callback> conditionCallbackCaptor =
                 ArgumentCaptor.forClass(Condition.Callback.class);
         verify(condition).addCallback(conditionCallbackCaptor.capture());
+
         final Condition.Callback conditionCallback = conditionCallbackCaptor.getValue();
-
-        when(condition.isConditionMet()).thenReturn(true);
-        conditionCallback.onConditionChanged(condition);
-        mExecutor.runAllReady();
-        verify(callback, never()).onConditionsChanged(true);
-
-        when(condition.isConditionMet()).thenReturn(false);
-        conditionCallback.onConditionChanged(condition);
-        mExecutor.runAllReady();
-        verify(callback, never()).onConditionsChanged(false);
+        verify(condition).removeCallback(conditionCallback);
     }
 
     @Test
@@ -245,14 +256,16 @@
                 mock(Monitor.Callback.class);
         final Monitor.Callback callback2 =
                 mock(Monitor.Callback.class);
-        mConditionMonitor.addCallback(callback1);
-        mConditionMonitor.addCallback(callback2);
+        final Monitor.Subscription.Token subscription1 =
+                mConditionMonitor.addSubscription(getDefaultBuilder(callback1).build());
+        final Monitor.Subscription.Token subscription2 =
+                mConditionMonitor.addSubscription(getDefaultBuilder(callback2).build());
 
-        mConditionMonitor.removeCallback(callback1);
+        mConditionMonitor.removeSubscription(subscription1);
         mExecutor.runAllReady();
         mConditions.forEach(condition -> verify(condition, never()).removeCallback(any()));
 
-        mConditionMonitor.removeCallback(callback2);
+        mConditionMonitor.removeSubscription(subscription2);
         mExecutor.runAllReady();
         mConditions.forEach(condition -> verify(condition).removeCallback(any()));
     }
@@ -261,7 +274,7 @@
     public void updateCallbacks_allConditionsMet_reportTrue() {
         final Monitor.Callback callback =
                 mock(Monitor.Callback.class);
-        mConditionMonitor.addCallback(callback);
+        mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
         clearInvocations(callback);
 
         mCondition1.fakeUpdateCondition(true);
@@ -276,7 +289,7 @@
     public void updateCallbacks_oneConditionStoppedMeeting_reportFalse() {
         final Monitor.Callback callback =
                 mock(Monitor.Callback.class);
-        mConditionMonitor.addCallback(callback);
+        mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
 
         mCondition1.fakeUpdateCondition(true);
         mCondition2.fakeUpdateCondition(true);
@@ -292,7 +305,7 @@
     public void updateCallbacks_shouldOnlyUpdateWhenValueChanges() {
         final Monitor.Callback callback =
                 mock(Monitor.Callback.class);
-        mConditionMonitor.addCallback(callback);
+        mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
         mExecutor.runAllReady();
         verify(callback).onConditionsChanged(false);
         clearInvocations(callback);
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index e37d7b0..6076eb1 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -1115,6 +1115,14 @@
                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "prompt_fgs_with_noti_on_long_running";
 
         /**
+         * The behavior for an app with a FGS, when the system detects it's running for
+         * a very long time, should we prompt the user.
+         * {@code true} - we'll show the prompt to user, {@code false} - we'll not show it.
+         */
+        static final String KEY_BG_PROMPT_FGS_ON_LONG_RUNNING =
+                DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "prompt_fgs_on_long_running";
+
+        /**
          * The list of packages to be exempted from all these background restrictions.
          */
         static final String KEY_BG_RESTRICTION_EXEMPTED_PACKAGES =
@@ -1154,6 +1162,11 @@
         static final boolean DEFAULT_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING = false;
 
         /**
+         * Default value to {@link #mBgPromptFgsOnLongRunning}.
+         */
+        static final boolean DEFAULT_BG_PROMPT_FGS_ON_LONG_RUNNING = true;
+
+        /**
          * Default value to {@link #mBgPromptFgsWithNotiToBgRestricted}.
          */
         final boolean mDefaultBgPromptFgsWithNotiToBgRestricted;
@@ -1191,6 +1204,11 @@
         volatile boolean mBgPromptFgsWithNotiOnLongRunning;
 
         /**
+         * @see #KEY_BG_PROMPT_FGS_ON_LONG_RUNNING.
+         */
+        volatile boolean mBgPromptFgsOnLongRunning;
+
+        /**
          * @see #KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED.
          */
         volatile boolean mBgPromptAbusiveAppsToBgRestricted;
@@ -1228,6 +1246,9 @@
                     case KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING:
                         updateBgPromptFgsWithNotiOnLongRunning();
                         break;
+                    case KEY_BG_PROMPT_FGS_ON_LONG_RUNNING:
+                        updateBgPromptFgsOnLongRunning();
+                        break;
                     case KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED:
                         updateBgPromptAbusiveAppToBgRestricted();
                         break;
@@ -1269,6 +1290,7 @@
             updateBgLongFgsNotificationMinimalInterval();
             updateBgPromptFgsWithNotiToBgRestricted();
             updateBgPromptFgsWithNotiOnLongRunning();
+            updateBgPromptFgsOnLongRunning();
             updateBgPromptAbusiveAppToBgRestricted();
             updateBgRestrictionExemptedPackages();
         }
@@ -1319,6 +1341,13 @@
                     DEFAULT_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING);
         }
 
+        private void updateBgPromptFgsOnLongRunning() {
+            mBgPromptFgsOnLongRunning = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    KEY_BG_PROMPT_FGS_ON_LONG_RUNNING,
+                    DEFAULT_BG_PROMPT_FGS_ON_LONG_RUNNING);
+        }
+
         private void updateBgPromptAbusiveAppToBgRestricted() {
             mBgPromptAbusiveAppsToBgRestricted = DeviceConfig.getBoolean(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1365,6 +1394,14 @@
             pw.print('=');
             pw.println(mBgLongFgsNotificationMinIntervalMs);
             pw.print(prefix);
+            pw.print(KEY_BG_PROMPT_FGS_ON_LONG_RUNNING);
+            pw.print('=');
+            pw.println(mBgPromptFgsOnLongRunning);
+            pw.print(prefix);
+            pw.print(KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING);
+            pw.print('=');
+            pw.println(mBgPromptFgsWithNotiOnLongRunning);
+            pw.print(prefix);
             pw.print(KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED);
             pw.print('=');
             pw.println(mBgPromptFgsWithNotiToBgRestricted);
@@ -2500,6 +2537,12 @@
                     ActivityManager.isLowRamDeviceStatic(),
                     mBgController.getRestrictionLevel(uid));
             PendingIntent pendingIntent;
+            if (!mBgController.mConstantsObserver.mBgPromptFgsOnLongRunning) {
+                if (DEBUG_BG_RESTRICTION_CONTROLLER) {
+                    Slog.i(TAG, "Long-running FGS prompt is disabled.");
+                }
+                return;
+            }
             if (!mBgController.mConstantsObserver.mBgPromptFgsWithNotiOnLongRunning
                     && mBgController.hasForegroundServiceNotifications(packageName, uid)) {
                 if (DEBUG_BG_RESTRICTION_CONTROLLER) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index a2048a3..92a8dcd 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2794,15 +2794,6 @@
         }
 
         int N = procs.size();
-        for (int i = 0; i < N; ++i) {
-            final ProcessRecord proc = procs.get(i).first;
-            try {
-                Process.setProcessFrozen(proc.getPid(), proc.uid, true);
-            } catch (Exception e) {
-                Slog.w(TAG, "Unable to freeze " + proc.getPid() + " " + proc.processName);
-            }
-        }
-
         for (int i=0; i<N; i++) {
             final Pair<ProcessRecord, Boolean> proc = procs.get(i);
             removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second,
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
index 78d55b9..f8d4b8f 100644
--- a/services/core/java/com/android/server/devicestate/DeviceState.java
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -18,6 +18,7 @@
 
 import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
 import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
@@ -48,9 +49,16 @@
      */
     public static final int FLAG_CANCEL_OVERRIDE_REQUESTS = 1 << 0;
 
+    /**
+     * Flag that indicates this device state is inaccessible for applications to be placed in. This
+     * could be a device-state where the {@link DEFAULT_DISPLAY} is not enabled.
+     */
+    public static final int FLAG_APP_INACCESSIBLE = 1 << 1;
+
     /** @hide */
     @IntDef(prefix = {"FLAG_"}, flag = true, value = {
             FLAG_CANCEL_OVERRIDE_REQUESTS,
+            FLAG_APP_INACCESSIBLE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DeviceStateFlags {}
@@ -97,7 +105,8 @@
 
     @Override
     public String toString() {
-        return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\'' + '}';
+        return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\''
+                + ", app_accessible=" + !hasFlag(FLAG_APP_INACCESSIBLE) + "}";
     }
 
     @Override
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 03e18a0..8e00ccf 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -40,6 +40,8 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
+import android.os.SystemProperties;
+import android.os.Trace;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -144,15 +146,30 @@
 
     private Set<Integer> mDeviceStatesAvailableForAppRequests;
 
+    @VisibleForTesting
+    interface SystemPropertySetter {
+        void setDebugTracingDeviceStateProperty(String value);
+    }
+    @NonNull
+    private final SystemPropertySetter mSystemPropertySetter;
+
     public DeviceStateManagerService(@NonNull Context context) {
         this(context, DeviceStatePolicy.Provider
                 .fromResources(context.getResources())
                 .instantiate(context));
     }
 
+    private DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) {
+        this(context, policy, (value) -> {
+            SystemProperties.set("debug.tracing.device_state", value);
+        });
+    }
+
     @VisibleForTesting
-    DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) {
+    DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy,
+                @NonNull SystemPropertySetter systemPropertySetter) {
         super(context);
+        mSystemPropertySetter = systemPropertySetter;
         // We use the DisplayThread because this service indirectly drives
         // display (on/off) and window (position) events through its callbacks.
         DisplayThread displayThread = DisplayThread.get();
@@ -462,6 +479,10 @@
 
             FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_STATE_CHANGED,
                     newState.getIdentifier(), !mCommittedState.isPresent());
+            String traceString = newState.getIdentifier() + ":" + newState.getName();
+            Trace.instantForTrack(
+                    Trace.TRACE_TAG_SYSTEM_SERVER, "DeviceStateChanged", traceString);
+            mSystemPropertySetter.setDebugTracingDeviceStateProperty(traceString);
 
             mCommittedState = Optional.of(newState);
             mPendingState = Optional.empty();
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index cf63b69..6fd8841 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -1057,7 +1057,17 @@
 
     public void recalculateSplines(boolean applyAdjustment, float[] adjustment) {
         mCurrentBrightnessMapper.recalculateSplines(applyAdjustment, adjustment);
-        updateAutoBrightness(true /*sendUpdate*/, false /*isManuallySet*/);
+
+        // If rbc is turned on, off or there is a change in strength, we want to reset the short
+        // term model. Since the nits range at which brightness now operates has changed due to
+        // RBC/strength change, any short term model based on the previous range should be
+        // invalidated.
+        resetShortTermModel();
+
+        // When rbc is turned on, we want to accommodate this change in the short term model.
+        if (applyAdjustment) {
+            setScreenBrightnessByUser(getAutomaticScreenBrightness());
+        }
     }
 
     private final class AutomaticBrightnessHandler extends Handler {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d3e2966..6285ef1 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -485,7 +485,7 @@
         mUiHandler = UiThread.getHandler();
         mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
         mLogicalDisplayMapper = new LogicalDisplayMapper(mContext, mDisplayDeviceRepo,
-                new LogicalDisplayListener(), mSyncRoot, mHandler, new DeviceStateToLayoutMap());
+                new LogicalDisplayListener(), mSyncRoot, mHandler);
         mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
         mBrightnessSynchronizer = new BrightnessSynchronizer(mContext);
         Resources resources = mContext.getResources();
@@ -945,8 +945,7 @@
 
     private DisplayInfo getDisplayInfoInternal(int displayId, int callingUid) {
         synchronized (mSyncRoot) {
-            final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId,
-                    /* includeDisabledDisplays= */ true);
+            final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
             if (display != null) {
                 final DisplayInfo info =
                         getDisplayInfoForFrameRateOverride(display.getFrameRateOverrides(),
@@ -2129,18 +2128,16 @@
     }
 
     void resetBrightnessConfigurations() {
-        synchronized (mSyncRoot) {
-            mPersistentDataStore.setBrightnessConfigurationForUser(null, mContext.getUserId(),
+        mPersistentDataStore.setBrightnessConfigurationForUser(null, mContext.getUserId(),
+                mContext.getPackageName());
+        mLogicalDisplayMapper.forEachLocked((logicalDisplay -> {
+            if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
+                return;
+            }
+            final String uniqueId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+            setBrightnessConfigurationForDisplayInternal(null, uniqueId, mContext.getUserId(),
                     mContext.getPackageName());
-            mLogicalDisplayMapper.forEachLocked((logicalDisplay -> {
-                if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
-                    return;
-                }
-                String uniqueId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
-                setBrightnessConfigurationForDisplayInternal(null, uniqueId, mContext.getUserId(),
-                        mContext.getPackageName());
-            }));
-        }
+        }));
     }
 
     void setAutoBrightnessLoggingEnabled(boolean enabled) {
@@ -2817,16 +2814,15 @@
         }
 
         /**
-         * Returns the list of all enabled display ids, and disabled ones if specified.
+         * Returns the list of all display ids.
          */
         @Override // Binder call
-        public int[] getDisplayIds(boolean includeDisabledDisplays) {
+        public int[] getDisplayIds() {
             final int callingUid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mSyncRoot) {
-                    return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid,
-                            includeDisabledDisplays);
+                    return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid);
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index a5bb716..d05a902 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -657,12 +657,6 @@
         }
         mIsRbcActive = mCdsi.isReduceBrightColorsActivated();
         mAutomaticBrightnessController.recalculateSplines(mIsRbcActive, adjustedNits);
-
-        // If rbc is turned on, off or there is a change in strength, we want to reset the short
-        // term model. Since the nits range at which brightness now operates has changed due to
-        // RBC/strength change, any short term model based on the previous range should be
-        // invalidated.
-        mAutomaticBrightnessController.resetShortTermModel();
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 34e8e75..70c9e23 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -79,12 +79,8 @@
     private static final int MSG_TRANSITION_TO_PENDING_DEVICE_STATE = 1;
 
     private static final int UPDATE_STATE_NEW = 0;
-    private static final int UPDATE_STATE_UPDATED = 1;
-    private static final int UPDATE_STATE_DISABLED = 2;
-
-    private static final int UPDATE_STATE_MASK = 0x3;
-
-    private static final int UPDATE_STATE_FLAG_TRANSITION = 0x100;
+    private static final int UPDATE_STATE_TRANSITION = 1;
+    private static final int UPDATE_STATE_UPDATED = 2;
 
     /**
      * Temporary display info, used for comparing display configurations.
@@ -170,7 +166,7 @@
 
     LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
             @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
-            @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap) {
+            @NonNull Handler handler) {
         mSyncRoot = syncRoot;
         mPowerManager = context.getSystemService(PowerManager.class);
         mInteractive = mPowerManager.isInteractive();
@@ -185,7 +181,7 @@
         mDeviceStatesOnWhichToSleep = toSparseBooleanArray(context.getResources().getIntArray(
                 com.android.internal.R.array.config_deviceStatesOnWhichToSleep));
         mDisplayDeviceRepo.addListener(this);
-        mDeviceStateToLayoutMap = deviceStateToLayoutMap;
+        mDeviceStateToLayoutMap = new DeviceStateToLayoutMap();
     }
 
     @Override
@@ -222,29 +218,10 @@
     }
 
     public LogicalDisplay getDisplayLocked(int displayId) {
-        return getDisplayLocked(displayId, /* includeDisabled= */ false);
-    }
-
-    LogicalDisplay getDisplayLocked(int displayId, boolean includeDisabled) {
-        LogicalDisplay display = mLogicalDisplays.get(displayId);
-        if (display != null && (display.isEnabled() || includeDisabled)) {
-            return display;
-        }
-        return null;
+        return mLogicalDisplays.get(displayId);
     }
 
     public LogicalDisplay getDisplayLocked(DisplayDevice device) {
-        return getDisplayLocked(device, /* includeDisabled= */ false);
-    }
-
-    /**
-     * Loops through the existing list of displays and returns one that is associated with the
-     * specified display device.
-     *
-     * @param device The display device that should be associated with the LogicalDisplay.
-     * @param includeDisabled True if this method should return disabled displays as well.
-     */
-    private LogicalDisplay getDisplayLocked(DisplayDevice device, boolean includeDisabled) {
         if (device == null) {
             return null;
         }
@@ -252,32 +229,18 @@
         for (int i = 0; i < count; i++) {
             final LogicalDisplay display = mLogicalDisplays.valueAt(i);
             if (display.getPrimaryDisplayDeviceLocked() == device) {
-                if (display.isEnabled() || includeDisabled) {
-                    return display;
-                } else {
-                    return null;
-                }
+                return display;
             }
         }
         return null;
     }
 
-    // Returns display Ids, defaults to enabled only.
     public int[] getDisplayIdsLocked(int callingUid) {
-        return getDisplayIdsLocked(callingUid, /* includeDisabledDisplays= */ false);
-    }
-
-    // Returns display Ids, specified whether enabled only, or all displays.
-    public int[] getDisplayIdsLocked(int callingUid, boolean includeDisabledDisplays) {
         final int count = mLogicalDisplays.size();
         int[] displayIds = new int[count];
         int n = 0;
         for (int i = 0; i < count; i++) {
             LogicalDisplay display = mLogicalDisplays.valueAt(i);
-            if (!includeDisabledDisplays && !display.isEnabled()) {
-                continue; // Ignore disabled displays.
-            }
-
             DisplayInfo info = display.getDisplayInfoLocked();
             if (info.hasAccess(callingUid)) {
                 displayIds[n++] = mLogicalDisplays.keyAt(i);
@@ -292,10 +255,7 @@
     public void forEachLocked(Consumer<LogicalDisplay> consumer) {
         final int count = mLogicalDisplays.size();
         for (int i = 0; i < count; i++) {
-            LogicalDisplay display = mLogicalDisplays.valueAt(i);
-            if (display.isEnabled()) {
-                consumer.accept(display);
-            }
+            consumer.accept(mLogicalDisplays.valueAt(i));
         }
     }
 
@@ -356,8 +316,7 @@
 
             // Find or create the LogicalDisplay to map the DisplayDevice to.
             final int logicalDisplayId = displayLayout.getLogicalDisplayId();
-            final LogicalDisplay logicalDisplay =
-                    getDisplayLocked(logicalDisplayId, /* includeDisabled= */ true);
+            final LogicalDisplay logicalDisplay = getDisplayLocked(logicalDisplayId);
             if (logicalDisplay == null) {
                 Slog.w(TAG, "The logical display (" + address + "), is not available"
                         + " for the display state " + deviceState);
@@ -493,7 +452,7 @@
     }
 
     /**
-     * Returns true if the device should be put to sleep or not.
+     * Returns if the device should be put to sleep or not.
      *
      * Includes a check to verify that the device state that we are moving to, {@code pendingState},
      * is the same as the physical state of the device, {@code baseState}. Different values for
@@ -639,12 +598,9 @@
             display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
 
             display.updateLocked(mDisplayDeviceRepo);
-            DisplayInfo newDisplayInfo = display.getDisplayInfoLocked();
-
-            final int storedState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
-            final int updateState = storedState & UPDATE_STATE_MASK;
-            final boolean isTransitioning = (storedState & UPDATE_STATE_FLAG_TRANSITION) != 0;
-            final boolean wasPreviouslyUpdated = updateState == UPDATE_STATE_UPDATED;
+            final DisplayInfo newDisplayInfo = display.getDisplayInfoLocked();
+            final int updateState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
+            final boolean wasPreviouslyUpdated = updateState != UPDATE_STATE_NEW;
 
             // The display is no longer valid and needs to be removed.
             if (!display.isValidLocked()) {
@@ -668,35 +624,6 @@
                 }
                 continue;
 
-            // The display has been newly disabled, we report this as a removed display but
-            // don't actually remove it from our internal list in LogicalDisplayMapper. The reason
-            // is that LogicalDisplayMapper assumes and relies on the fact that every DisplayDevice
-            // has a LogicalDisplay wrapper, but certain displays that are unusable (like the inner
-            // display on a folded foldable device) are not available for use by the system and
-            // we keep them hidden. To do this, we mark those LogicalDisplays as "disabled".
-            // Also, if the display is in TRANSITION but was previously reported as disabled
-            // then keep it unreported.
-            } else if (!display.isEnabled()
-                    || (display.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION
-                        && updateState == UPDATE_STATE_DISABLED)) {
-                mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_DISABLED);
-
-                // If we never told anyone about this display, nothing to do
-                if (!wasPreviouslyUpdated) {
-                    continue;
-                }
-
-                // Remove from group
-                final DisplayGroup displayGroup = getDisplayGroupLocked(
-                        getDisplayGroupIdFromDisplayIdLocked(displayId));
-                if (displayGroup != null) {
-                    displayGroup.removeDisplayLocked(display);
-                }
-
-                Slog.i(TAG, "Removing (disabled) display: " + displayId);
-                mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_REMOVED);
-                continue;
-
             // The display is new.
             } else if (!wasPreviouslyUpdated) {
                 Slog.i(TAG, "Adding new display: " + displayId + ": " + newDisplayInfo);
@@ -716,7 +643,7 @@
                 mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
 
             // The display is involved in a display layout transition
-            } else if (isTransitioning) {
+            } else if (updateState == UPDATE_STATE_TRANSITION) {
                 mLogicalDisplaysToUpdate.put(displayId,
                         LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
 
@@ -790,7 +717,7 @@
             }
 
             final int id = mLogicalDisplaysToUpdate.keyAt(i);
-            final LogicalDisplay display = getDisplayLocked(id, /* includeDisabled= */ true);
+            final LogicalDisplay display = getDisplayLocked(id);
             if (DEBUG) {
                 final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
                 final String uniqueId = device == null ? "null" : device.getUniqueId();
@@ -798,7 +725,7 @@
                         + " with device=" + uniqueId);
             }
             mListener.onLogicalDisplayEventLocked(display, msg);
-            if (msg == LOGICAL_DISPLAY_EVENT_REMOVED && !display.isValidLocked()) {
+            if (msg == LOGICAL_DISPLAY_EVENT_REMOVED) {
                 // We wait until we sent the EVENT_REMOVED event before actually removing the
                 // display.
                 mLogicalDisplays.delete(id);
@@ -918,8 +845,7 @@
             if (isTransitioning) {
                 setDisplayPhase(logicalDisplay, phase);
                 if (phase == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) {
-                    int oldState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
-                    mUpdatedLogicalDisplays.put(displayId, oldState | UPDATE_STATE_FLAG_TRANSITION);
+                    mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_TRANSITION);
                 }
             }
         }
@@ -953,15 +879,14 @@
             // Now that we have a display-device, we need a LogicalDisplay to map it to. Find the
             // right one, if it doesn't exist, create a new one.
             final int logicalDisplayId = displayLayout.getLogicalDisplayId();
-            LogicalDisplay newDisplay =
-                    getDisplayLocked(logicalDisplayId, /* includeDisabled= */ true);
+            LogicalDisplay newDisplay = getDisplayLocked(logicalDisplayId);
             if (newDisplay == null) {
                 newDisplay = createNewLogicalDisplayLocked(
-                        /* displayDevice= */ null, logicalDisplayId);
+                        null /*displayDevice*/, logicalDisplayId);
             }
 
             // Now swap the underlying display devices between the old display and the new display
-            final LogicalDisplay oldDisplay = getDisplayLocked(device, /* includeDisabled= */ true);
+            final LogicalDisplay oldDisplay = getDisplayLocked(device);
             if (newDisplay != oldDisplay) {
                 newDisplay.swapDisplaysLocked(oldDisplay);
             }
@@ -978,14 +903,13 @@
      * Creates a new logical display for the specified device and display Id and adds it to the list
      * of logical displays.
      *
-     * @param displayDevice The displayDevice to associate with the LogicalDisplay.
+     * @param device The device to associate with the LogicalDisplay.
      * @param displayId The display ID to give the new display. If invalid, a new ID is assigned.
      * @return The new logical display if created, null otherwise.
      */
-    private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice displayDevice,
-            int displayId) {
+    private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice device, int displayId) {
         final int layerStack = assignLayerStackLocked(displayId);
-        final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, displayDevice);
+        final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
         display.updateLocked(mDisplayDeviceRepo);
         mLogicalDisplays.put(displayId, display);
         setDisplayPhase(display, LogicalDisplay.DISPLAY_PHASE_ENABLED);
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index 9fddc76..181c39e 100644
--- a/services/core/java/com/android/server/pm/AppsFilterImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java
@@ -418,7 +418,9 @@
         } else if (changed) {
             invalidateCache("grantImplicitAccess: " + recipientUid + " -> " + visibleUid);
         }
-        onChanged();
+        if (changed) {
+            onChanged();
+        }
         return changed;
     }
 
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 30de9ba..259ca65 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -2686,7 +2686,7 @@
         if (Process.isSdkSandboxUid(callingUid)) {
             int clientAppUid = Process.getAppUidForSdkSandboxUid(callingUid);
             // SDK sandbox should be able to see it's client app
-            if (clientAppUid == UserHandle.getUid(userId, ps.getAppId())) {
+            if (ps != null && clientAppUid == UserHandle.getUid(userId, ps.getAppId())) {
                 return false;
             }
         }
@@ -2698,7 +2698,7 @@
         final boolean callerIsInstantApp = instantAppPkgName != null;
         if (ps == null) {
             // pretend the application exists, but, needs to be filtered
-            return callerIsInstantApp;
+            return callerIsInstantApp || Process.isSdkSandboxUid(callingUid);
         }
         // if the target and caller are the same application, don't filter
         if (isCallerSameApp(ps.getPackageName(), callingUid)) {
@@ -3089,6 +3089,19 @@
     }
 
     public boolean filterAppAccess(int uid, int callingUid) {
+        if (Process.isSdkSandboxUid(uid)) {
+            // Sdk sandbox instance should be able to see itself.
+            if (callingUid == uid) {
+                return false;
+            }
+            final int clientAppUid = Process.getAppUidForSdkSandboxUid(uid);
+            // Client app of this sdk sandbox process should be able to see it.
+            if (clientAppUid == uid) {
+                return false;
+            }
+            // Nobody else should be able to see the sdk sandbox process.
+            return true;
+        }
         final int userId = UserHandle.getUserId(uid);
         final int appId = UserHandle.getAppId(uid);
         final Object setting = mSettings.getSettingBase(appId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4f8c792..94e8ec5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2859,12 +2859,15 @@
         mDexOptHelper.performPackageDexOptUpgradeIfNeeded();
     }
 
-
     private void notifyPackageUseInternal(String packageName, int reason) {
         long time = System.currentTimeMillis();
-        commitPackageStateMutation(null, packageName, packageState -> {
-            packageState.setLastPackageUsageTime(reason, time);
-        });
+        synchronized (mLock) {
+            final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
+            if (pkgSetting == null) {
+                return;
+            }
+            pkgSetting.getPkgState().setLastPackageUsageTimeInMills(reason, time);
+        }
     }
 
     /*package*/ DexManager getDexManager() {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index cc1c943..0ae92b4 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -911,6 +911,11 @@
         grantSystemFixedPermissionsToSystemPackage(pm,
                 MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, userId,
                 NEARBY_DEVICES_PERMISSIONS);
+
+        // Ad Service
+        String commonServiceAction = "android.adservices.AD_SERVICES_COMMON_SERVICE";
+        grantPermissionsToSystemPackage(pm, getDefaultSystemHandlerServicePackage(pm,
+                        commonServiceAction, userId), userId, NOTIFICATION_PERMISSIONS);
     }
 
     private String getDefaultSystemHandlerActivityPackageForCategory(PackageManagerWrapper pm,
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
index 7bd720a..5251fe0 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
@@ -79,7 +79,10 @@
             return this;
         }
         getLastPackageUsageTimeInMills()[reason] = time;
-        mPackageSetting.onChanged();
+        // TODO(b/236180425): This method does not notify snapshot changes because it's called too
+        //  frequently, causing too many re-takes. This should be moved to a separate data structure
+        //  or merged with the general UsageStats to avoid tracking heavily mutated data in the
+        //  package data snapshot.
         return this;
     }
 
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 17a5fd0..9b7d19a 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -95,6 +95,7 @@
     private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
     private static final String CONFIG_FILE_NAME = "device_state_configuration.xml";
     private static final String FLAG_CANCEL_OVERRIDE_REQUESTS = "FLAG_CANCEL_OVERRIDE_REQUESTS";
+    private static final String FLAG_APP_INACCESSIBLE = "FLAG_APP_INACCESSIBLE";
 
     /** Interface that allows reading the device state configuration. */
     interface ReadableConfig {
@@ -145,6 +146,9 @@
                                 case FLAG_CANCEL_OVERRIDE_REQUESTS:
                                     flags |= DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
                                     break;
+                                case FLAG_APP_INACCESSIBLE:
+                                    flags |= DeviceState.FLAG_APP_INACCESSIBLE;
+                                    break;
                                 default:
                                     Slog.w(TAG, "Parsed unknown flag with name: "
                                             + configFlagString);
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index dd0ec94..43d62aa 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -21,6 +21,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
 import android.os.AsyncTask;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.util.Slog;
 import android.webkit.UserPackage;
@@ -265,10 +266,12 @@
                 // Either the current relro creation  isn't done yet, or the new relro creatioin
                 // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
                 webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
-                Slog.e(TAG, "Timed out waiting for relro creation, relros started "
+                String timeoutError = "Timed out waiting for relro creation, relros started "
                         + mNumRelroCreationsStarted
                         + " relros finished " + mNumRelroCreationsFinished
-                        + " package dirty? " + mWebViewPackageDirty);
+                        + " package dirty? " + mWebViewPackageDirty;
+                Slog.e(TAG, timeoutError);
+                Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, timeoutError);
             }
         }
         if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index af0c69f..b3b392c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4295,9 +4295,15 @@
                 mTransitionController.collect(tStartingWindow);
                 tStartingWindow.reparent(this, POSITION_TOP);
 
-                // Propagate other interesting state between the tokens. If the old token is displayed,
-                // we should immediately force the new one to be displayed. If it is animating, we need
-                // to move that animation to the new one.
+                // Clear the frozen insets state when transferring the existing starting window to
+                // the next target activity.  In case the frozen state from a trampoline activity
+                // affecting the starting window frame computation to see the window being
+                // clipped if the rotation change during the transition animation.
+                tStartingWindow.clearFrozenInsetsState();
+
+                // Propagate other interesting state between the tokens. If the old token is
+                // displayed, we should immediately force the new one to be displayed. If it is
+                // animating, we need to move that animation to the new one.
                 if (fromActivity.allDrawn) {
                     allDrawn = true;
                 }
@@ -9729,6 +9735,7 @@
                 false /*isNotInRecents*/,
                 record.mThumbnailAdapter != null ? record.mThumbnailAdapter.mCapturedLeash : null,
                 record.mStartBounds, task.getTaskInfo(), checkEnterPictureInPictureAppOpsState());
+        target.setShowBackdrop(record.mShowBackdrop);
         target.hasAnimatingParent = record.hasAnimatingParent();
         return target;
     }
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index caaaf47..53f2c71 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -314,6 +314,10 @@
         setAppTransitionState(APP_STATE_TIMEOUT);
     }
 
+    @ColorInt int getNextAppTransitionBackgroundColor() {
+        return mNextAppTransitionBackgroundColor;
+    }
+
     HardwareBuffer getAppTransitionThumbnailHeader(WindowContainer container) {
         AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
                 container.hashCode());
@@ -523,6 +527,95 @@
         return TransitionAnimation.loadAnimationSafely(context, resId, TAG);
     }
 
+    static int mapOpenCloseTransitTypes(int transit, boolean enter) {
+        int animAttr = 0;
+        switch (transit) {
+            case TRANSIT_OLD_ACTIVITY_OPEN:
+            case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN:
+                animAttr = enter
+                        ? WindowAnimation_activityOpenEnterAnimation
+                        : WindowAnimation_activityOpenExitAnimation;
+                break;
+            case TRANSIT_OLD_ACTIVITY_CLOSE:
+            case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE:
+                animAttr = enter
+                        ? WindowAnimation_activityCloseEnterAnimation
+                        : WindowAnimation_activityCloseExitAnimation;
+                break;
+            case TRANSIT_OLD_TASK_OPEN:
+                animAttr = enter
+                        ? WindowAnimation_taskOpenEnterAnimation
+                        : WindowAnimation_taskOpenExitAnimation;
+                break;
+            case TRANSIT_OLD_TASK_CLOSE:
+                animAttr = enter
+                        ? WindowAnimation_taskCloseEnterAnimation
+                        : WindowAnimation_taskCloseExitAnimation;
+                break;
+            case TRANSIT_OLD_TASK_TO_FRONT:
+                animAttr = enter
+                        ? WindowAnimation_taskToFrontEnterAnimation
+                        : WindowAnimation_taskToFrontExitAnimation;
+                break;
+            case TRANSIT_OLD_TASK_TO_BACK:
+                animAttr = enter
+                        ? WindowAnimation_taskToBackEnterAnimation
+                        : WindowAnimation_taskToBackExitAnimation;
+                break;
+            case TRANSIT_OLD_WALLPAPER_OPEN:
+                animAttr = enter
+                        ? WindowAnimation_wallpaperOpenEnterAnimation
+                        : WindowAnimation_wallpaperOpenExitAnimation;
+                break;
+            case TRANSIT_OLD_WALLPAPER_CLOSE:
+                animAttr = enter
+                        ? WindowAnimation_wallpaperCloseEnterAnimation
+                        : WindowAnimation_wallpaperCloseExitAnimation;
+                break;
+            case TRANSIT_OLD_WALLPAPER_INTRA_OPEN:
+                animAttr = enter
+                        ? WindowAnimation_wallpaperIntraOpenEnterAnimation
+                        : WindowAnimation_wallpaperIntraOpenExitAnimation;
+                break;
+            case TRANSIT_OLD_WALLPAPER_INTRA_CLOSE:
+                animAttr = enter
+                        ? WindowAnimation_wallpaperIntraCloseEnterAnimation
+                        : WindowAnimation_wallpaperIntraCloseExitAnimation;
+                break;
+            case TRANSIT_OLD_TASK_OPEN_BEHIND:
+                animAttr = enter
+                        ? WindowAnimation_launchTaskBehindSourceAnimation
+                        : WindowAnimation_launchTaskBehindTargetAnimation;
+                break;
+            // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
+            //  need new TaskFragment transition.
+            case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
+                animAttr = enter
+                        ? WindowAnimation_activityOpenEnterAnimation
+                        : WindowAnimation_activityOpenExitAnimation;
+                break;
+            // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
+            //  need new TaskFragment transition.
+            case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
+                animAttr = enter
+                        ? WindowAnimation_activityCloseEnterAnimation
+                        : WindowAnimation_activityCloseExitAnimation;
+                break;
+            case TRANSIT_OLD_DREAM_ACTIVITY_OPEN:
+                animAttr = enter
+                        ? WindowAnimation_dreamActivityOpenEnterAnimation
+                        : WindowAnimation_dreamActivityOpenExitAnimation;
+                break;
+            case TRANSIT_OLD_DREAM_ACTIVITY_CLOSE:
+                animAttr = enter
+                        ? 0
+                        : WindowAnimation_dreamActivityCloseExitAnimation;
+                break;
+        }
+
+        return animAttr;
+    }
+
     @Nullable
     Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
         return mTransitionAnimation.loadAnimationAttr(lp, animAttr, transit);
@@ -773,94 +866,11 @@
                     "applyAnimation: anim=%s transit=%s isEntrance=%b Callers=%s",
                     a, appTransitionOldToString(transit), enter, Debug.getCallers(3));
         } else {
-            int animAttr = 0;
-            switch (transit) {
-                case TRANSIT_OLD_ACTIVITY_OPEN:
-                case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN:
-                    animAttr = enter
-                            ? WindowAnimation_activityOpenEnterAnimation
-                            : WindowAnimation_activityOpenExitAnimation;
-                    break;
-                case TRANSIT_OLD_ACTIVITY_CLOSE:
-                case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE:
-                    animAttr = enter
-                            ? WindowAnimation_activityCloseEnterAnimation
-                            : WindowAnimation_activityCloseExitAnimation;
-                    break;
-                case TRANSIT_OLD_TASK_OPEN:
-                    animAttr = enter
-                            ? WindowAnimation_taskOpenEnterAnimation
-                            : WindowAnimation_taskOpenExitAnimation;
-                    break;
-                case TRANSIT_OLD_TASK_CLOSE:
-                    animAttr = enter
-                            ? WindowAnimation_taskCloseEnterAnimation
-                            : WindowAnimation_taskCloseExitAnimation;
-                    break;
-                case TRANSIT_OLD_TASK_TO_FRONT:
-                    animAttr = enter
-                            ? WindowAnimation_taskToFrontEnterAnimation
-                            : WindowAnimation_taskToFrontExitAnimation;
-                    break;
-                case TRANSIT_OLD_TASK_TO_BACK:
-                    animAttr = enter
-                            ? WindowAnimation_taskToBackEnterAnimation
-                            : WindowAnimation_taskToBackExitAnimation;
-                    break;
-                case TRANSIT_OLD_WALLPAPER_OPEN:
-                    animAttr = enter
-                            ? WindowAnimation_wallpaperOpenEnterAnimation
-                            : WindowAnimation_wallpaperOpenExitAnimation;
-                    break;
-                case TRANSIT_OLD_WALLPAPER_CLOSE:
-                    animAttr = enter
-                            ? WindowAnimation_wallpaperCloseEnterAnimation
-                            : WindowAnimation_wallpaperCloseExitAnimation;
-                    break;
-                case TRANSIT_OLD_WALLPAPER_INTRA_OPEN:
-                    animAttr = enter
-                            ? WindowAnimation_wallpaperIntraOpenEnterAnimation
-                            : WindowAnimation_wallpaperIntraOpenExitAnimation;
-                    break;
-                case TRANSIT_OLD_WALLPAPER_INTRA_CLOSE:
-                    animAttr = enter
-                            ? WindowAnimation_wallpaperIntraCloseEnterAnimation
-                            : WindowAnimation_wallpaperIntraCloseExitAnimation;
-                    break;
-                case TRANSIT_OLD_TASK_OPEN_BEHIND:
-                    animAttr = enter
-                            ? WindowAnimation_launchTaskBehindSourceAnimation
-                            : WindowAnimation_launchTaskBehindTargetAnimation;
-                    break;
-                // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
-                //  need new TaskFragment transition.
-                case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
-                    animAttr = enter
-                            ? WindowAnimation_activityOpenEnterAnimation
-                            : WindowAnimation_activityOpenExitAnimation;
-                    break;
-                // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
-                //  need new TaskFragment transition.
-                case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
-                    animAttr = enter
-                            ? WindowAnimation_activityCloseEnterAnimation
-                            : WindowAnimation_activityCloseExitAnimation;
-                    break;
-                case TRANSIT_OLD_DREAM_ACTIVITY_OPEN:
-                    animAttr = enter
-                            ? WindowAnimation_dreamActivityOpenEnterAnimation
-                            : WindowAnimation_dreamActivityOpenExitAnimation;
-                    break;
-                case TRANSIT_OLD_DREAM_ACTIVITY_CLOSE:
-                    animAttr = enter
-                            ? 0
-                            : WindowAnimation_dreamActivityCloseExitAnimation;
-                    break;
-            }
-
+            int animAttr = mapOpenCloseTransitTypes(transit, enter);
             a = animAttr == 0 ? null : (canCustomizeAppTransition
-                    ? loadAnimationAttr(lp, animAttr, transit)
-                    : mTransitionAnimation.loadDefaultAnimationAttr(animAttr, transit));
+                ? loadAnimationAttr(lp, animAttr, transit)
+                : mTransitionAnimation.loadDefaultAnimationAttr(animAttr, transit));
+
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                     "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b "
                             + " canCustomizeAppTransition=%b Callers=%s",
@@ -1011,18 +1021,19 @@
     }
 
     void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
-        overridePendingAppTransitionRemote(remoteAnimationAdapter, false /* sync */);
+        overridePendingAppTransitionRemote(remoteAnimationAdapter, false /* sync */,
+                false /* isActivityEmbedding*/);
     }
 
     void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter,
-            boolean sync) {
+            boolean sync, boolean isActivityEmbedding) {
         ProtoLog.i(WM_DEBUG_APP_TRANSITIONS, "Override pending remote transitionSet=%b adapter=%s",
                         isTransitionSet(), remoteAnimationAdapter);
         if (isTransitionSet() && !mNextAppTransitionIsSync) {
             clear();
             mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
             mRemoteAnimationController = new RemoteAnimationController(mService, mDisplayContent,
-                    remoteAnimationAdapter, mHandler);
+                    remoteAnimationAdapter, mHandler, isActivityEmbedding);
             mNextAppTransitionIsSync = sync;
         }
     }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index fbb7dac..3d66122 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -693,7 +693,8 @@
         if (adapter == null) {
             return false;
         }
-        mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(adapter);
+        mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(
+                adapter, false /* sync */, true /*isActivityEmbedding*/);
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                 "Override with TaskFragment remote animation for transit=%s",
                 AppTransition.appTransitionOldToString(transit));
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2ea6ce5..2351dd7 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -130,7 +130,6 @@
 import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
 import static com.android.server.wm.DisplayContentProto.SLEEP_TOKENS;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -757,8 +756,7 @@
         }
         if (w.mAttrs.type == TYPE_INPUT_METHOD_DIALOG && mImeLayeringTarget != null
                 && !mImeLayeringTarget.getRequestedVisibility(ITYPE_IME)
-                && mImeLayeringTarget.isAnimating(PARENTS | TRANSITION,
-                ANIMATION_TYPE_APP_TRANSITION)) {
+                && !mImeLayeringTarget.isVisibleRequested()) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index fd06313..7ca38b8 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -99,19 +99,28 @@
         state.setRoundedCorners(roundedCorners);
         state.setPrivacyIndicatorBounds(indicatorBounds);
         state.getDisplayCutoutSafe(safe);
-        if (!cutout.isEmpty()) {
+        if (safe.left > unrestricted.left) {
             state.getSource(ITYPE_LEFT_DISPLAY_CUTOUT).setFrame(
                     unrestricted.left, unrestricted.top, safe.left, unrestricted.bottom);
+        } else {
+            state.removeSource(ITYPE_LEFT_DISPLAY_CUTOUT);
+        }
+        if (safe.top > unrestricted.top) {
             state.getSource(ITYPE_TOP_DISPLAY_CUTOUT).setFrame(
                     unrestricted.left, unrestricted.top, unrestricted.right, safe.top);
+        } else {
+            state.removeSource(ITYPE_TOP_DISPLAY_CUTOUT);
+        }
+        if (safe.right < unrestricted.right) {
             state.getSource(ITYPE_RIGHT_DISPLAY_CUTOUT).setFrame(
                     safe.right, unrestricted.top, unrestricted.right, unrestricted.bottom);
+        } else {
+            state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT);
+        }
+        if (safe.bottom < unrestricted.bottom) {
             state.getSource(ITYPE_BOTTOM_DISPLAY_CUTOUT).setFrame(
                     unrestricted.left, safe.bottom, unrestricted.right, unrestricted.bottom);
         } else {
-            state.removeSource(ITYPE_LEFT_DISPLAY_CUTOUT);
-            state.removeSource(ITYPE_TOP_DISPLAY_CUTOUT);
-            state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT);
             state.removeSource(ITYPE_BOTTOM_DISPLAY_CUTOUT);
         }
         return true;
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 4a0a6e3..ad158c7 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -22,6 +22,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Point;
@@ -57,7 +58,7 @@
  */
 class RemoteAnimationController implements DeathRecipient {
     private static final String TAG = TAG_WITH_CLASS_NAME
-                    ? "RemoteAnimationController" : TAG_WM;
+            ? "RemoteAnimationController" : TAG_WM;
     private static final long TIMEOUT_MS = 10000;
 
     private final WindowManagerService mService;
@@ -72,35 +73,40 @@
     private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable");
 
     private FinishedCallback mFinishedCallback;
+    private final boolean mIsActivityEmbedding;
     private boolean mCanceled;
     private boolean mLinkedToDeathOfRunner;
     @Nullable
     private Runnable mOnRemoteAnimationReady;
 
     RemoteAnimationController(WindowManagerService service, DisplayContent displayContent,
-            RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
+            RemoteAnimationAdapter remoteAnimationAdapter, Handler handler,
+            boolean isActivityEmbedding) {
         mService = service;
         mDisplayContent = displayContent;
         mRemoteAnimationAdapter = remoteAnimationAdapter;
         mHandler = handler;
+        mIsActivityEmbedding = isActivityEmbedding;
     }
 
     /**
      * Creates an animation record for each individual {@link WindowContainer}.
      *
      * @param windowContainer The windows to animate.
-     * @param position The position app bounds relative to its parent.
-     * @param localBounds The bounds of the app relative to its parent.
-     * @param endBounds The end bounds after the transition, in screen coordinates.
-     * @param startBounds The start bounds before the transition, in screen coordinates.
+     * @param position        The position app bounds relative to its parent.
+     * @param localBounds     The bounds of the app relative to its parent.
+     * @param endBounds       The end bounds after the transition, in screen coordinates.
+     * @param startBounds     The start bounds before the transition, in screen coordinates.
+     * @param showBackdrop    To show background behind a window during animation.
      * @return The record representing animation(s) to run on the app.
      */
     RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
-            Point position, Rect localBounds, Rect endBounds, Rect startBounds) {
+            Point position, Rect localBounds, Rect endBounds, Rect startBounds,
+            boolean showBackdrop) {
         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",
                 windowContainer);
         final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position,
-                localBounds, endBounds, startBounds);
+                localBounds, endBounds, startBounds, showBackdrop);
         mPendingAnimations.add(adapters);
         return adapters;
     }
@@ -111,6 +117,15 @@
     }
 
     /**
+     * We use isFromActivityEmbedding() in the server process to tell if we're running an
+     * Activity Embedding type remote animation, where animations are driven by the client.
+     * This is currently supporting features like showBackdrop where we need to load App XML.
+     */
+    public boolean isFromActivityEmbedding() {
+        return mIsActivityEmbedding;
+    }
+
+    /**
      * Called when the transition is ready to be started, and all leashes have been set up.
      */
     void goodToGo(@WindowManager.TransitionOldType int transit) {
@@ -416,30 +431,37 @@
         RemoteAnimationTarget mTarget;
         final WindowContainer mWindowContainer;
         final Rect mStartBounds;
+        final boolean mShowBackdrop;
+        @ColorInt int mBackdropColor = 0;
         private @RemoteAnimationTarget.Mode int mMode = RemoteAnimationTarget.MODE_CHANGING;
 
         RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
-                Rect endBounds, Rect startBounds) {
+                Rect endBounds, Rect startBounds, boolean showBackdrop) {
             mWindowContainer = windowContainer;
+            mShowBackdrop = showBackdrop;
             if (startBounds != null) {
                 mStartBounds = new Rect(startBounds);
                 mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
-                        mStartBounds);
+                        mStartBounds, mShowBackdrop);
                 if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
                     final Rect thumbnailLocalBounds = new Rect(startBounds);
                     thumbnailLocalBounds.offsetTo(0, 0);
                     // Snapshot is located at (0,0) of the animation leash. It doesn't have size
                     // change, so the startBounds is its end bounds, and no start bounds for it.
                     mThumbnailAdapter = new RemoteAnimationAdapterWrapper(this, new Point(0, 0),
-                            thumbnailLocalBounds, startBounds, new Rect());
+                            thumbnailLocalBounds, startBounds, new Rect(), mShowBackdrop);
                 }
             } else {
                 mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
-                        new Rect());
+                        new Rect(), mShowBackdrop);
                 mStartBounds = null;
             }
         }
 
+        void setBackDropColor(@ColorInt int backdropColor) {
+            mBackdropColor = backdropColor;
+        }
+
         RemoteAnimationTarget createRemoteAnimationTarget() {
             if (mAdapter == null
                     || mAdapter.mCapturedFinishCallback == null
@@ -481,14 +503,27 @@
         final Rect mLocalBounds;
         final Rect mEndBounds = new Rect();
         final Rect mStartBounds = new Rect();
+        final boolean mShowBackdrop;
 
         RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
-                Rect localBounds, Rect endBounds, Rect startBounds) {
+                Rect localBounds, Rect endBounds, Rect startBounds, boolean showBackdrop) {
             mRecord = record;
             mPosition.set(position.x, position.y);
             mLocalBounds = localBounds;
             mEndBounds.set(endBounds);
             mStartBounds.set(startBounds);
+            mShowBackdrop = showBackdrop;
+        }
+
+        @Override
+        @ColorInt
+        public int getBackgroundColor() {
+            return mRecord.mBackdropColor;
+        }
+
+        @Override
+        public boolean getShowBackground() {
+            return mShowBackdrop;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index fc1b893..64905c8 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1858,6 +1858,11 @@
         if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_UNDEFINED) {
             nextPersistTaskBounds = newParentConfig.windowConfiguration.persistTaskBounds();
         }
+        // Only restore to the last non-fullscreen bounds when the requested override bounds
+        // have not been explicitly set already.
+        nextPersistTaskBounds &=
+                (getRequestedOverrideConfiguration().windowConfiguration.getBounds() == null
+                || getRequestedOverrideConfiguration().windowConfiguration.getBounds().isEmpty());
         if (!prevPersistTaskBounds && nextPersistTaskBounds
                 && mLastNonFullscreenBounds != null && !mLastNonFullscreenBounds.isEmpty()) {
             // Bypass onRequestedOverrideConfigurationChanged here to avoid infinite loop.
@@ -4427,13 +4432,13 @@
                     : WINDOWING_MODE_FULLSCREEN;
         }
         if (currentMode == WINDOWING_MODE_PINNED) {
+            // In the case that we've disabled affecting the SysUI flags as a part of seamlessly
+            // transferring the transform on the leash to the task, reset this state once we're
+            // moving out of pip
+            setCanAffectSystemUiFlags(true);
             mRootWindowContainer.notifyActivityPipModeChanged(this, null);
         }
         if (likelyResolvedMode == WINDOWING_MODE_PINNED) {
-            // In the case that we've disabled affecting the SysUI flags as a part of seamlessly
-            // transferring the transform on the leash to the task, reset this state once we've
-            // actually entered pip
-            setCanAffectSystemUiFlags(true);
             if (taskDisplayArea.getRootPinnedTask() != null) {
                 // Can only have 1 pip at a time, so replace an existing pip
                 taskDisplayArea.getRootPinnedTask().dismissPip();
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index d8404a7..3c0cac0 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -305,6 +305,16 @@
         return mFlags;
     }
 
+    @VisibleForTesting
+    SurfaceControl.Transaction getStartTransaction() {
+        return mStartTransaction;
+    }
+
+    @VisibleForTesting
+    SurfaceControl.Transaction getFinishTransaction() {
+        return mFinishTransaction;
+    }
+
     /** Starts collecting phase. Once this starts, all relevant surface operations are sync. */
     void startCollecting(long timeoutMs) {
         if (mState != STATE_PENDING) {
@@ -771,6 +781,8 @@
         }
 
         mState = STATE_PLAYING;
+        mStartTransaction = transaction;
+        mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
         mController.moveToPlaying(this);
 
         if (dc.isKeyguardLocked()) {
@@ -856,8 +868,6 @@
         if (controller != null && mTargets.contains(dc)) {
             controller.setupStartTransaction(transaction);
         }
-        mStartTransaction = transaction;
-        mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
         buildFinishTransaction(mFinishTransaction, info.getRootLeash());
         if (mController.getTransitionPlayer() != null) {
             mController.dispatchLegacyAppTransitionStarting(info);
diff --git a/services/core/java/com/android/server/wm/TransitionTracer.java b/services/core/java/com/android/server/wm/TransitionTracer.java
index 192b9ab..b1951e0 100644
--- a/services/core/java/com/android/server/wm/TransitionTracer.java
+++ b/services/core/java/com/android/server/wm/TransitionTracer.java
@@ -23,8 +23,10 @@
 import static com.android.server.wm.shell.ChangeInfo.TRANSIT_MODE;
 import static com.android.server.wm.shell.ChangeInfo.WINDOW_IDENTIFIER;
 import static com.android.server.wm.shell.Transition.CHANGE;
+import static com.android.server.wm.shell.Transition.FINISH_TRANSACTION_ID;
 import static com.android.server.wm.shell.Transition.FLAGS;
 import static com.android.server.wm.shell.Transition.ID;
+import static com.android.server.wm.shell.Transition.START_TRANSACTION_ID;
 import static com.android.server.wm.shell.Transition.STATE;
 import static com.android.server.wm.shell.Transition.TIMESTAMP;
 import static com.android.server.wm.shell.Transition.TRANSITION_TYPE;
@@ -82,6 +84,13 @@
             outputStream.write(TRANSITION_TYPE, transition.mType);
             outputStream.write(STATE, transition.getState());
             outputStream.write(FLAGS, transition.getFlags());
+            if (transition.getStartTransaction() != null) {
+                outputStream.write(START_TRANSACTION_ID, transition.getStartTransaction().getId());
+            }
+            if (transition.getFinishTransaction() != null) {
+                outputStream.write(FINISH_TRANSACTION_ID,
+                        transition.getFinishTransaction().getId());
+            }
 
             for (int i = 0; i < transition.mChanges.size(); ++i) {
                 final WindowContainer window = transition.mChanges.keyAt(i);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 51adb16..59aed83 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2940,18 +2940,34 @@
         getAnimationPosition(mTmpPoint);
         mTmpRect.offsetTo(0, 0);
 
-        final RemoteAnimationController controller =
-                getDisplayContent().mAppTransition.getRemoteAnimationController();
+        final AppTransition appTransition = getDisplayContent().mAppTransition;
+        final RemoteAnimationController controller = appTransition.getRemoteAnimationController();
         final boolean isChanging = AppTransition.isChangeTransitOld(transit) && enter
                 && isChangingAppTransition();
 
         // Delaying animation start isn't compatible with remote animations at all.
         if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
+            // Here we load App XML in order to read com.android.R.styleable#Animation_showBackdrop.
+            boolean showBackdrop = false;
+            // Optionally set backdrop color if App explicitly provides it through
+            // {@link Activity#overridePendingTransition(int, int, int)}.
+            @ColorInt int backdropColor = 0;
+            if (controller.isFromActivityEmbedding()) {
+                final int animAttr = AppTransition.mapOpenCloseTransitTypes(transit, enter);
+                final Animation a = animAttr != 0
+                        ? appTransition.loadAnimationAttr(lp, animAttr, transit) : null;
+                showBackdrop = a != null && a.getShowBackdrop();
+                backdropColor = appTransition.getNextAppTransitionBackgroundColor();
+            }
             final Rect localBounds = new Rect(mTmpRect);
             localBounds.offsetTo(mTmpPoint.x, mTmpPoint.y);
             final RemoteAnimationController.RemoteAnimationRecord adapters =
-                    controller.createRemoteAnimationRecord(this, mTmpPoint, localBounds,
-                            screenBounds, (isChanging ? mSurfaceFreezer.mFreezeBounds : null));
+                    controller.createRemoteAnimationRecord(
+                            this, mTmpPoint, localBounds, screenBounds,
+                            (isChanging ? mSurfaceFreezer.mFreezeBounds : null), showBackdrop);
+            if (backdropColor != 0) {
+                adapters.setBackDropColor(backdropColor);
+            }
             if (!isChanging) {
                 adapters.setMode(enter
                         ? RemoteAnimationTarget.MODE_OPENING
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 54cf57f..1d93c89 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -328,7 +328,8 @@
                 }
                 adapter.setCallingPidUid(caller.mPid, caller.mUid);
                 dc.prepareAppTransition(type);
-                dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */);
+                dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */,
+                        false /* isActivityEmbedding */);
                 syncId = startSyncWithOrganizer(callback);
                 applyTransaction(t, syncId, null /* transition */, caller);
                 setSyncReady(syncId);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b4ec347..46cf5c9 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -879,7 +879,9 @@
      * {@link InsetsStateController#notifyInsetsChanged}.
      */
     boolean isReadyToDispatchInsetsState() {
-        return isVisibleRequested() && mFrozenInsetsState == null;
+        final boolean visible = shouldCheckTokenVisibleRequested()
+                ? isVisibleRequested() : isVisible();
+        return visible && mFrozenInsetsState == null;
     }
 
     void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9f7cbf3..05ea9cc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -9292,11 +9292,11 @@
             }
 
             // Check profile owner first as that is what most likely is set.
-            if (isSupervisionComponent(poComponent)) {
+            if (isSupervisionComponentLocked(poComponent)) {
                 return poComponent;
             }
 
-            if (isSupervisionComponent(doComponent)) {
+            if (isSupervisionComponentLocked(doComponent)) {
                 return doComponent;
             }
 
@@ -9304,7 +9304,26 @@
         }
     }
 
-    private boolean isSupervisionComponent(@Nullable ComponentName who) {
+    /**
+     * Returns if the specified component is the supervision component.
+     */
+    @Override
+    public boolean isSupervisionComponent(@NonNull ComponentName who) {
+        if (!mHasFeature) {
+            return false;
+        }
+        synchronized (getLockObject()) {
+            if (mConstants.USE_TEST_ADMIN_AS_SUPERVISION_COMPONENT) {
+                final CallerIdentity caller = getCallerIdentity();
+                if (isAdminTestOnlyLocked(who, caller.getUserId())) {
+                    return true;
+                }
+            }
+            return isSupervisionComponentLocked(who);
+        }
+    }
+
+    private boolean isSupervisionComponentLocked(@Nullable ComponentName who) {
         if (who == null) {
             return false;
         }
@@ -9508,7 +9527,7 @@
                     "Cannot set the profile owner on a user which is already set-up");
 
             if (!mIsWatch) {
-                if (!isSupervisionComponent(owner)) {
+                if (!isSupervisionComponentLocked(owner)) {
                     throw new IllegalStateException("Unable to set non-default profile owner"
                             + " post-setup " + owner);
                 }
@@ -12102,8 +12121,8 @@
         synchronized (getLockObject()) {
             // Allow testOnly admins to bypass supervision config requirement.
             Preconditions.checkCallAuthorization(isAdminTestOnlyLocked(who, caller.getUserId())
-                    || isSupervisionComponent(caller.getComponentName()), "Admin %s is not the "
-                    + "default supervision component", caller.getComponentName());
+                    || isSupervisionComponentLocked(caller.getComponentName()), "Admin %s is not "
+                    + "the default supervision component", caller.getComponentName());
             DevicePolicyData policy = getUserData(caller.getUserId());
             policy.mSecondaryLockscreenEnabled = enabled;
             saveSettingsLocked(caller.getUserId());
@@ -13004,7 +13023,7 @@
                     return false;
                 }
 
-                return isSupervisionComponent(admin.info.getComponent());
+                return isSupervisionComponentLocked(admin.info.getComponent());
             }
         }
 
diff --git a/services/proguard.flags b/services/proguard.flags
index bad02b4..c648f7d 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -68,6 +68,9 @@
 # TODO(b/210510433): Revisit and consider generating from frameworks/base/core/res/res/values/config.xml.
 -keep,allowoptimization,allowaccessmodification public class com.android.server.notification.** implements com.android.server.notification.NotificationSignalExtractor
 
+# OEM provided DisplayAreaPolicy.Provider defined in frameworks/base/core/res/res/values/config.xml.
+-keep,allowoptimization,allowaccessmodification class com.android.server.wm.** implements com.android.server.wm.DisplayAreaPolicy$Provider
+
 # JNI keep rules
 # TODO(b/210510433): Revisit and fix with @Keep, or consider auto-generating from
 # frameworks/base/services/core/jni/onload.cpp.
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index fa8d569..f02571f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -1141,6 +1141,7 @@
         DeviceConfigSession<Long> longRunningFGSWindow = null;
         DeviceConfigSession<Long> longRunningFGSThreshold = null;
         DeviceConfigSession<Boolean> longRunningFGSWithNotification = null;
+        DeviceConfigSession<Boolean> longRunningFGS = null;
 
         try {
             longRunningFGSMonitor = new DeviceConfigSession<>(
@@ -1171,6 +1172,13 @@
                     ConstantsObserver.DEFAULT_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING);
             longRunningFGSWithNotification.set(true);
 
+            longRunningFGS = new DeviceConfigSession<>(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    ConstantsObserver.KEY_BG_PROMPT_FGS_ON_LONG_RUNNING,
+                    DeviceConfig::getBoolean,
+                    ConstantsObserver.DEFAULT_BG_PROMPT_FGS_ON_LONG_RUNNING);
+            longRunningFGS.set(true);
+
             // Basic case
             mAppFGSTracker.onForegroundServiceStateChanged(testPkgName1, testUid1,
                     testPid1, true);
@@ -1214,6 +1222,23 @@
                     testPid2, false);
             checkNotificationGone(testPkgName2, timeout(windowMs), notificationId);
 
+            // Turn OFF the notification.
+            longRunningFGS.set(false);
+            clearInvocations(mInjector.getNotificationManager());
+            mBgRestrictionController.resetRestrictionSettings();
+            // Start the FGS again.
+            mAppFGSTracker.onForegroundServiceStateChanged(testPkgName2, testUid2,
+                    testPid2, true);
+            // Verify we do NOT have the notification.
+            checkNotificationShown(
+                    new String[] {testPkgName2}, timeout(windowMs * 2).times(0), false);
+            // Stop this FGS
+            mAppFGSTracker.onForegroundServiceStateChanged(testPkgName2, testUid2,
+                    testPid2, false);
+
+            // Turn it back ON.
+            longRunningFGS.set(true);
+
             // Start over with concurrent cases.
             clearInvocations(mInjector.getNotificationManager());
             mBgRestrictionController.resetRestrictionSettings();
@@ -1306,6 +1331,7 @@
             closeIfNotNull(longRunningFGSWindow);
             closeIfNotNull(longRunningFGSThreshold);
             closeIfNotNull(longRunningFGSWithNotification);
+            closeIfNotNull(longRunningFGS);
         }
     }
 
@@ -1332,6 +1358,7 @@
         DeviceConfigSession<Long> mediaPlaybackFGSThreshold = null;
         DeviceConfigSession<Long> locationFGSThreshold = null;
         DeviceConfigSession<Boolean> longRunningFGSWithNotification = null;
+        DeviceConfigSession<Boolean> longRunningFGS = null;
 
         doReturn(testPkgName1).when(mInjector).getPackageName(testPid1);
         doReturn(testPkgName2).when(mInjector).getPackageName(testPid2);
@@ -1379,6 +1406,13 @@
                     ConstantsObserver.DEFAULT_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING);
             longRunningFGSWithNotification.set(true);
 
+            longRunningFGS = new DeviceConfigSession<>(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    ConstantsObserver.KEY_BG_PROMPT_FGS_ON_LONG_RUNNING,
+                    DeviceConfig::getBoolean,
+                    ConstantsObserver.DEFAULT_BG_PROMPT_FGS_ON_LONG_RUNNING);
+            longRunningFGS.set(true);
+
             // Long-running FGS with type "location", but ran for a very short time.
             runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
                     FOREGROUND_SERVICE_TYPE_LOCATION, 0, null, OP_NONE, null, null,
@@ -1487,6 +1521,7 @@
             closeIfNotNull(mediaPlaybackFGSThreshold);
             closeIfNotNull(locationFGSThreshold);
             closeIfNotNull(longRunningFGSWithNotification);
+            closeIfNotNull(longRunningFGS);
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8014d25..388170b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3360,9 +3360,11 @@
             assertThat(dpmi.isActiveSupervisionApp(uid)).isTrue();
             assertThat(dpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(user))
                         .isEqualTo(admin1);
+            assertThat(dpm.isSupervisionComponent(admin1)).isTrue();
         } else {
             assertThat(dpmi.isActiveSupervisionApp(uid)).isFalse();
             assertThat(dpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(user)).isNull();
+            assertThat(dpm.isSupervisionComponent(admin1)).isFalse();
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index fe3034d..038cbc0 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -76,12 +76,15 @@
     private TestDeviceStatePolicy mPolicy;
     private TestDeviceStateProvider mProvider;
     private DeviceStateManagerService mService;
+    private TestSystemPropertySetter mSysPropSetter;
 
     @Before
     public void setup() {
         mProvider = new TestDeviceStateProvider();
         mPolicy = new TestDeviceStatePolicy(mProvider);
-        mService = new DeviceStateManagerService(InstrumentationRegistry.getContext(), mPolicy);
+        mSysPropSetter = new TestSystemPropertySetter();
+        mService = new DeviceStateManagerService(InstrumentationRegistry.getContext(), mPolicy,
+                mSysPropSetter);
 
         // Necessary to allow us to check for top app process id in tests
         mService.mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
@@ -107,6 +110,8 @@
     public void baseStateChanged() {
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mSysPropSetter.getValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
@@ -115,6 +120,8 @@
         flushHandler();
         assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mSysPropSetter.getValue(),
+                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
@@ -128,6 +135,8 @@
         flushHandler();
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
+        assertEquals(mSysPropSetter.getValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
@@ -136,6 +145,8 @@
         flushHandler();
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
+        assertEquals(mSysPropSetter.getValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
@@ -157,6 +168,8 @@
 
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mSysPropSetter.getValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
@@ -170,6 +183,8 @@
 
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mSysPropSetter.getValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
@@ -182,6 +197,8 @@
 
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mSysPropSetter.getValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
                 OTHER_DEVICE_STATE);
@@ -193,6 +210,8 @@
         // supported.
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mSysPropSetter.getValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE);
 
@@ -211,6 +230,8 @@
 
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mSysPropSetter.getValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
                 OTHER_DEVICE_STATE);
@@ -223,6 +244,8 @@
         // supported.
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mSysPropSetter.getValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
                 OTHER_DEVICE_STATE);
@@ -315,6 +338,8 @@
                 TestDeviceStateManagerCallback.STATUS_ACTIVE);
         // Committed state changes as there is a requested override.
         assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
+        assertEquals(mSysPropSetter.getValue(),
+                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
@@ -333,6 +358,8 @@
                 TestDeviceStateManagerCallback.STATUS_CANCELED);
         // Committed state is set back to the requested state once the override is cleared.
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
+        assertEquals(mSysPropSetter.getValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertFalse(mService.getOverrideState().isPresent());
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
@@ -385,9 +412,10 @@
         // callback.
         assertEquals(callback.getLastNotifiedStatus(secondRequestToken),
                 TestDeviceStateManagerCallback.STATUS_UNKNOWN);
-
         assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.of(DEFAULT_DEVICE_STATE));
+        assertEquals(mSysPropSetter.getValue(),
+                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
@@ -397,6 +425,8 @@
 
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mSysPropSetter.getValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
@@ -412,6 +442,8 @@
 
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
+        assertEquals(mSysPropSetter.getValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 DEFAULT_DEVICE_STATE.getIdentifier());
@@ -458,6 +490,8 @@
         // Committed state changes as there is a requested override.
         assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+        assertEquals(mSysPropSetter.getValue(),
+                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
         assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
@@ -471,6 +505,8 @@
         // Committed state is set back to the requested state once the override is cleared.
         assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
         assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
+        assertEquals(mSysPropSetter.getValue(),
+                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
         assertFalse(mService.getOverrideState().isPresent());
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
                 OTHER_DEVICE_STATE.getIdentifier());
@@ -495,6 +531,8 @@
                 TestDeviceStateManagerCallback.STATUS_ACTIVE);
         // Committed state changes as there is a requested override.
         assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
+        assertEquals(mSysPropSetter.getValue(),
+                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
@@ -509,6 +547,8 @@
         // Committed state is set back to the requested state as the override state is no longer
         // supported.
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
+        assertEquals(mSysPropSetter.getValue(),
+                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertFalse(mService.getOverrideState().isPresent());
         assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
@@ -673,4 +713,18 @@
             return mLastNotifiedStatus.getOrDefault(requestToken, STATUS_UNKNOWN);
         }
     }
+
+    private static final class TestSystemPropertySetter implements
+            DeviceStateManagerService.SystemPropertySetter {
+        private String mValue;
+
+        @Override
+        public void setDebugTracingDeviceStateProperty(String value) {
+            mValue = value;
+        }
+
+        public String getValue() {
+            return mValue;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index b0c52f1..9d82f1a 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyFloat;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -258,29 +259,36 @@
     @Test
     public void testRecalculateSplines() throws Exception {
         // Enabling the light sensor, and setting the ambient lux to 1000
+        int currentLux = 1000;
         ArgumentCaptor<SensorEventListener> listenerCaptor =
                 ArgumentCaptor.forClass(SensorEventListener.class);
         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
         SensorEventListener listener = listenerCaptor.getValue();
-        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, currentLux));
 
-        //Setting the brightnessFloat to 0.5f
-        float currentBrightnessFloat = 0.5f;
-        when(mBrightnessMappingStrategy.getBrightness(1000,
-                null, ApplicationInfo.CATEGORY_UNDEFINED)).thenReturn(currentBrightnessFloat);
+        // User sets brightness to 0.5f
+        when(mBrightnessMappingStrategy.getBrightness(currentLux,
+                null, ApplicationInfo.CATEGORY_UNDEFINED)).thenReturn(0.5f);
         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
-                currentBrightnessFloat /* brightness */, false /* userChangedBrightness */,
-                0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
+                0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
+                false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
 
-        // Adjusting spline, and accordingly remapping the current 0.5f brightnessFloat to 0.3f
-        float updatedBrightnessFloat = 0.3f;
-        when(mBrightnessMappingStrategy.getBrightness(1000,
-                null, ApplicationInfo.CATEGORY_UNDEFINED)).thenReturn(updatedBrightnessFloat);
-        float[] adjustments = new float[]{0.2f, 0.5f};
+        //Recalculating the spline with RBC enabled, verifying that the short term model is reset,
+        //and the interaction is learnt in short term model
+        float[] adjustments = new float[]{0.2f, 0.6f};
         mController.recalculateSplines(true, adjustments);
+        verify(mBrightnessMappingStrategy).clearUserDataPoints();
         verify(mBrightnessMappingStrategy).recalculateSplines(true, adjustments);
-        assertEquals(mController.getAutomaticScreenBrightness(), updatedBrightnessFloat, EPSILON);
+        verify(mBrightnessMappingStrategy, times(2)).addUserDataPoint(currentLux, 0.5f);
+
+        clearInvocations(mBrightnessMappingStrategy);
+
+        // Verify short term model is not learnt when RBC is disabled
+        mController.recalculateSplines(false, adjustments);
+        verify(mBrightnessMappingStrategy).clearUserDataPoints();
+        verify(mBrightnessMappingStrategy).recalculateSplines(false, adjustments);
+        verifyNoMoreInteractions(mBrightnessMappingStrategy);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 5f1ff6b..1e97c1c 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -287,7 +287,7 @@
 
         when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
 
-        final int[] displayIds = bs.getDisplayIds(/* includeDisabled= */ false);
+        final int displayIds[] = bs.getDisplayIds();
         final int size = displayIds.length;
         assertTrue(size > 0);
 
@@ -297,9 +297,7 @@
         );
         for (int i = 0; i < size; i++) {
             DisplayInfo info = bs.getDisplayInfo(displayIds[i]);
-            if (info != null) {
-                assertTrue(expectedDisplayTypeToViewPortTypeMapping.keySet().contains(info.type));
-            }
+            assertTrue(expectedDisplayTypeToViewPortTypeMapping.keySet().contains(info.type));
         }
 
         displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -1176,8 +1174,7 @@
             DisplayManagerService.BinderService displayManagerBinderService,
             FakeDisplayDevice displayDevice) {
 
-        final int[] displayIds = displayManagerBinderService.getDisplayIds(
-                /* includeDisabled= */ false);
+        final int[] displayIds = displayManagerBinderService.getDisplayIds();
         assertTrue(displayIds.length > 0);
         int displayId = Display.INVALID_DISPLAY;
         for (int i = 0; i < displayIds.length; i++) {
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 5b13145..cc68ba8 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -33,7 +33,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -54,8 +53,6 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.display.layout.Layout;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -82,7 +79,6 @@
     private TestLooper mLooper;
     private Handler mHandler;
     private PowerManager mPowerManager;
-    private DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy;
 
     @Mock LogicalDisplayMapper.Listener mListenerMock;
     @Mock Context mContextMock;
@@ -137,11 +133,8 @@
 
         mLooper = new TestLooper();
         mHandler = new Handler(mLooper.getLooper());
-
-        mDeviceStateToLayoutMapSpy = spy(new DeviceStateToLayoutMap());
         mLogicalDisplayMapper = new LogicalDisplayMapper(mContextMock, mDisplayDeviceRepo,
-                mListenerMock, new DisplayManagerService.SyncRoot(), mHandler,
-                mDeviceStateToLayoutMapSpy);
+                mListenerMock, new DisplayManagerService.SyncRoot(), mHandler);
     }
 
 
@@ -420,86 +413,6 @@
                 /* isBootCompleted= */true));
     }
 
-    @Test public void testEnabledAndDisabledDisplays() {
-        DisplayAddress displayAddressOne = new TestUtils.TestDisplayAddress();
-        DisplayAddress displayAddressTwo = new TestUtils.TestDisplayAddress();
-        DisplayAddress displayAddressThree = new TestUtils.TestDisplayAddress();
-
-        TestDisplayDevice device1 = createDisplayDevice(displayAddressOne, Display.TYPE_INTERNAL,
-                600, 800,
-                DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
-        TestDisplayDevice device2 = createDisplayDevice(displayAddressTwo, Display.TYPE_INTERNAL,
-                200, 800,
-                DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
-        TestDisplayDevice device3 = createDisplayDevice(displayAddressThree, Display.TYPE_INTERNAL,
-                600, 900, DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
-
-        Layout threeDevicesEnabledLayout = new Layout();
-        threeDevicesEnabledLayout.createDisplayLocked(
-                displayAddressOne,
-                /* isDefault= */ true,
-                /* isEnabled= */ true);
-
-        threeDevicesEnabledLayout.createDisplayLocked(
-                displayAddressTwo,
-                /* isDefault= */ false,
-                /* isEnabled= */ true);
-        threeDevicesEnabledLayout.createDisplayLocked(
-                displayAddressThree,
-                /* isDefault= */ false,
-                /* isEnabled= */ true);
-
-        when(mDeviceStateToLayoutMapSpy.get(DeviceStateToLayoutMap.STATE_DEFAULT))
-                .thenReturn(threeDevicesEnabledLayout);
-
-        LogicalDisplay display1 = add(device1);
-        LogicalDisplay display2 = add(device2);
-        LogicalDisplay display3 = add(device3);
-
-        // ensure 3 displays are returned
-        int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID);
-        assertEquals(3, ids.length);
-        Arrays.sort(ids);
-        assertEquals(DEFAULT_DISPLAY, ids[0]);
-
-        Layout oneDeviceEnabledLayout = new Layout();
-        oneDeviceEnabledLayout.createDisplayLocked(
-                display1.getDisplayInfoLocked().address,
-                /* isDefault= */ true,
-                /* isEnabled= */ true);
-        oneDeviceEnabledLayout.createDisplayLocked(
-                display2.getDisplayInfoLocked().address,
-                /* isDefault= */ false,
-                /* isEnabled= */ false);
-        oneDeviceEnabledLayout.createDisplayLocked(
-                display3.getDisplayInfoLocked().address,
-                /* isDefault= */ false,
-                /* isEnabled= */ false);
-
-        when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(oneDeviceEnabledLayout);
-        when(mDeviceStateToLayoutMapSpy.get(1)).thenReturn(threeDevicesEnabledLayout);
-
-        mLogicalDisplayMapper
-                .setDeviceStateLocked(0, false);
-        mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
-        final int[] allDisplayIds = mLogicalDisplayMapper.getDisplayIdsLocked(
-                Process.SYSTEM_UID, false);
-        mLooper.dispatchAll();
-
-        // ensure only one display is returned
-        assertEquals(1, allDisplayIds.length);
-
-        mLogicalDisplayMapper
-                .setDeviceStateLocked(1, false);
-        mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
-        final int[] threeDisplaysEnabled = mLogicalDisplayMapper.getDisplayIdsLocked(
-                Process.SYSTEM_UID, false);
-        mLooper.dispatchAll();
-
-        // ensure all three displays are returned
-        assertEquals(3, threeDisplaysEnabled.length);
-    }
-
     /////////////////
     // Helper Methods
     /////////////////
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 0331051..2477f6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2820,12 +2820,18 @@
                 true, false, false, false);
         waitUntilHandlersIdle();
 
+        final WindowState startingWindow = activityTop.mStartingWindow;
+        assertNotNull(startingWindow);
+
         // Make the top one invisible, and try transferring the starting window from the top to the
         // bottom one.
         activityTop.setVisibility(false, false);
         activityBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded();
         waitUntilHandlersIdle();
 
+        // Expect getFrozenInsetsState will be null when transferring the starting window.
+        assertNull(startingWindow.getFrozenInsetsState());
+
         // Assert that the bottom window now has the starting window.
         assertNoStartingWindow(activityTop);
         assertHasStartingWindow(activityBottom);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index a19b19e..d737963 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -78,10 +78,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
-import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 
@@ -2426,7 +2424,7 @@
     @Test
     public void testImeMenuDialogFocusWhenImeLayeringTargetChanges() {
         final WindowState imeMenuDialog =
-                createWindow(mImeWindow, TYPE_INPUT_METHOD_DIALOG, "imeMenuDialog");
+                createWindow(null, TYPE_INPUT_METHOD_DIALOG, "imeMenuDialog");
         makeWindowVisibleAndDrawn(imeMenuDialog, mImeWindow);
         assertTrue(imeMenuDialog.canReceiveKeys());
         mDisplayContent.setInputMethodWindowLocked(mImeWindow);
@@ -2439,13 +2437,11 @@
         doReturn(true).when(imeAppTarget).getRequestedVisibility(ITYPE_IME);
         assertEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
 
-        // Verify imeMenuDialog doesn't be focused window if the next IME target does not
-        // request IME visible.
+        // Verify imeMenuDialog doesn't be focused window if the next IME target is closing.
         final WindowState nextImeAppTarget =
                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "nextImeAppTarget");
-        spyOn(nextImeAppTarget);
-        doReturn(true).when(nextImeAppTarget).isAnimating(PARENTS | TRANSITION,
-                ANIMATION_TYPE_APP_TRANSITION);
+        makeWindowVisibleAndDrawn(nextImeAppTarget);
+        nextImeAppTarget.mActivityRecord.commitVisibility(false, false);
         mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
         assertNotEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index a4851ad5..204c7e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -78,19 +78,25 @@
 
 /**
  * Build/Install/Run:
- *  atest WmTests:RemoteAnimationControllerTest
+ * atest WmTests:RemoteAnimationControllerTest
  */
 @SmallTest
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class RemoteAnimationControllerTest extends WindowTestsBase {
 
-    @Mock SurfaceControl mMockLeash;
-    @Mock SurfaceControl mMockThumbnailLeash;
-    @Mock Transaction mMockTransaction;
-    @Mock OnAnimationFinishedCallback mFinishedCallback;
-    @Mock OnAnimationFinishedCallback mThumbnailFinishedCallback;
-    @Mock IRemoteAnimationRunner mMockRunner;
+    @Mock
+    SurfaceControl mMockLeash;
+    @Mock
+    SurfaceControl mMockThumbnailLeash;
+    @Mock
+    Transaction mMockTransaction;
+    @Mock
+    OnAnimationFinishedCallback mFinishedCallback;
+    @Mock
+    OnAnimationFinishedCallback mThumbnailFinishedCallback;
+    @Mock
+    IRemoteAnimationRunner mMockRunner;
     private RemoteAnimationAdapter mAdapter;
     private RemoteAnimationController mController;
     private final OffsettableClock mClock = new OffsettableClock.Stopped();
@@ -104,7 +110,8 @@
         mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50, true /* changeNeedsSnapshot */);
         mAdapter.setCallingPidUid(123, 456);
         runWithScissors(mWm.mH, () -> mHandler = new TestHandler(null, mClock), 0);
-        mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter, mHandler);
+        mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter,
+                mHandler, false /*isActivityEmbedding*/);
     }
 
     private WindowState createAppOverlayWindow() {
@@ -116,13 +123,47 @@
     }
 
     @Test
+    public void testForwardsShowBackdrop() throws Exception {
+        final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+        mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+        final WindowState overlayWin = createAppOverlayWindow();
+        try {
+            final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+                    win.mActivityRecord,
+                    new Point(50, 100), null, new Rect(50, 100, 150, 150), null,
+                    true /* showBackdrop */).mAdapter;
+            adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+                    mFinishedCallback);
+            mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
+            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+                    ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
+                    finishedCaptor.capture());
+            assertEquals(1, appsCaptor.getValue().length);
+            final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+            assertTrue(app.showBackdrop);
+        } finally {
+            mDisplayContent.mOpeningApps.clear();
+        }
+    }
+
+    @Test
     public void testRun() throws Exception {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mDisplayContent.mOpeningApps.add(win.mActivityRecord);
         final WindowState overlayWin = createAppOverlayWindow();
         try {
-            final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
-                    new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+            final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+                    win.mActivityRecord,
+                    new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -161,8 +202,9 @@
     @Test
     public void testCancel() throws Exception {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
-        final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
-                new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+        final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+                win.mActivityRecord,
+                new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -174,8 +216,9 @@
     @Test
     public void testTimeout() throws Exception {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
-        final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
-                new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+        final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+                win.mActivityRecord,
+                new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -196,7 +239,7 @@
                     "testWin");
             final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
                     win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150),
-                    null).mAdapter;
+                    null, false).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -228,7 +271,7 @@
     public void testNotReallyStarted() throws Exception {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mController.createRemoteAnimationRecord(win.mActivityRecord,
-                new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
+                new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false);
         mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
         verify(mMockRunner, never()).onAnimationStart(anyInt(), any(), any(), any(), any());
         verify(mMockRunner).onAnimationCancelled();
@@ -239,9 +282,10 @@
         final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1");
         final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2");
         mController.createRemoteAnimationRecord(win1.mActivityRecord,
-                new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
-        final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win2.mActivityRecord,
-                new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+                new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false);
+        final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+                win2.mActivityRecord,
+                new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -264,8 +308,9 @@
     @Test
     public void testRemovedBeforeStarted() throws Exception {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
-        final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
-                new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+        final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+                win.mActivityRecord,
+                new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         win.mActivityRecord.removeImmediately();
@@ -309,7 +354,7 @@
         try {
             final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
                     win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150),
-                    new Rect(0, 0, 200, 200));
+                    new Rect(0, 0, 200, 200), false);
             assertNotNull(record.mThumbnailAdapter);
             ((AnimationAdapter) record.mAdapter)
                     .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION,
@@ -363,7 +408,7 @@
         try {
             final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
                     win.mActivityRecord, new Point(0, 0), null, new Rect(0, 0, 200, 200),
-                    new Rect(50, 100, 150, 150));
+                    new Rect(50, 100, 150, 150), false);
             assertNotNull(record.mThumbnailAdapter);
             ((AnimationAdapter) record.mAdapter)
                     .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION,
@@ -417,7 +462,7 @@
         try {
             final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
                     win.mActivityRecord, new Point(100, 100), null, new Rect(150, 150, 400, 400),
-                    new Rect(50, 100, 150, 150));
+                    new Rect(50, 100, 150, 150), false);
             assertNotNull(record.mThumbnailAdapter);
             ((AnimationAdapter) record.mAdapter)
                     .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION,
@@ -474,8 +519,9 @@
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mDisplayContent.mOpeningApps.add(win.mActivityRecord);
         try {
-            final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
-                    new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+            final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+                    win.mActivityRecord,
+                    new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -506,8 +552,9 @@
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mDisplayContent.mOpeningApps.add(win.mActivityRecord);
         try {
-            final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
-                    new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+            final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+                    win.mActivityRecord,
+                    new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -543,7 +590,7 @@
         try {
             final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
                     win.mActivityRecord, new Point(50, 100), null,
-                    new Rect(50, 100, 150, 150), null).mAdapter;
+                    new Rect(50, 100, 150, 150), null, false).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY);
@@ -593,7 +640,7 @@
         try {
             final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
                     win.mActivityRecord, new Point(50, 100), null,
-                    new Rect(50, 100, 150, 150), null).mAdapter;
+                    new Rect(50, 100, 150, 150), null, false).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER);
@@ -727,7 +774,7 @@
             mDisplayContent.mOpeningApps.add(win2.mActivityRecord);
             final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
                     win2.mActivityRecord, new Point(50, 100), null,
-                    new Rect(50, 100, 150, 150), null).mAdapter;
+                    new Rect(50, 100, 150, 150), null, false).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
 
@@ -757,7 +804,7 @@
 
         final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
                 win.mActivityRecord, new Point(50, 100), null,
-                new Rect(50, 100, 150, 150), null).mAdapter;
+                new Rect(50, 100, 150, 150), null, false).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         mController.goodToGo(transit);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index c1ab823..fb7400b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -935,6 +935,15 @@
         assertTrue(app.isReadyToDispatchInsetsState());
         mDisplayContent.getInsetsStateController().notifyInsetsChanged();
         verify(app).notifyInsetsChanged();
+
+        // Verify that invisible non-activity window won't dispatch insets changed.
+        final WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay");
+        makeWindowVisible(overlay);
+        assertTrue(overlay.isReadyToDispatchInsetsState());
+        overlay.mHasSurface = false;
+        assertFalse(overlay.isReadyToDispatchInsetsState());
+        mDisplayContent.getInsetsStateController().notifyInsetsChanged();
+        assertFalse(overlay.getWindowFrames().hasInsetsChanged());
     }
 
     @UseTestDisplay(addWindows = {W_INPUT_METHOD, W_ACTIVITY})