Merge "Keep mCanAffectSystemUiFlags to be false while in PiP" 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/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/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/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/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/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/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c5f6b78..d29655a 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() {
@@ -2853,6 +2862,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/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/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/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/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/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/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/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/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/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/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 9a35970..b3b392c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -9735,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/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/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/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/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/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);