Merge "Support excluding persistent UI" into main
diff --git a/DREAM_MANAGER_OWNERS b/DREAM_MANAGER_OWNERS
new file mode 100644
index 0000000..48bde60
--- /dev/null
+++ b/DREAM_MANAGER_OWNERS
@@ -0,0 +1 @@
+brycelee@google.com
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 74b34fb..412f2b7 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -104,6 +104,18 @@
     ],
 }
 
+genrule {
+    name: "framework-minus-apex.ravenwood.keep_all",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
+    cmd: "cp $(in) $(out)",
+    srcs: [
+        ":framework-minus-apex.ravenwood-base{hoststubgen_keep_all.txt}",
+    ],
+    out: [
+        "hoststubgen_framework-minus-apex_keep_all.txt",
+    ],
+}
+
 java_library {
     name: "services.core-for-hoststubgen",
     installable: false, // host only jar.
@@ -189,6 +201,18 @@
     ],
 }
 
+genrule {
+    name: "services.core.ravenwood.keep_all",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
+    cmd: "cp $(in) $(out)",
+    srcs: [
+        ":services.core.ravenwood-base{hoststubgen_keep_all.txt}",
+    ],
+    out: [
+        "hoststubgen_services.core_keep_all.txt",
+    ],
+}
+
 java_library {
     name: "services.core.ravenwood-jarjar",
     installable: false,
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 7724c23..92543b1 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -32,6 +32,11 @@
     public boolean topActivityEligibleForLetterboxEducation;
 
     /**
+     * Whether the letterbox education is enabled
+     */
+    public boolean isLetterboxEducationEnabled;
+
+    /**
      * Whether the direct top activity is in size compat mode on foreground.
      */
     public boolean topActivityInSizeCompat;
@@ -178,6 +183,7 @@
                     == that.topActivityEligibleForUserAspectRatioButton
                 && topActivityEligibleForLetterboxEducation
                     == that.topActivityEligibleForLetterboxEducation
+                && isLetterboxEducationEnabled == that.isLetterboxEducationEnabled
                 && topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition
                 && topActivityLetterboxHorizontalPosition
                     == that.topActivityLetterboxHorizontalPosition
@@ -192,6 +198,7 @@
      * Reads the AppCompatTaskInfo from a parcel.
      */
     void readFromParcel(Parcel source) {
+        isLetterboxEducationEnabled = source.readBoolean();
         topActivityInSizeCompat = source.readBoolean();
         topActivityEligibleForLetterboxEducation = source.readBoolean();
         isLetterboxDoubleTapEnabled = source.readBoolean();
@@ -212,6 +219,7 @@
      */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(isLetterboxEducationEnabled);
         dest.writeBoolean(topActivityInSizeCompat);
         dest.writeBoolean(topActivityEligibleForLetterboxEducation);
         dest.writeBoolean(isLetterboxDoubleTapEnabled);
@@ -232,6 +240,7 @@
         return "AppCompatTaskInfo { topActivityInSizeCompat=" + topActivityInSizeCompat
                 + " topActivityEligibleForLetterboxEducation= "
                 + topActivityEligibleForLetterboxEducation
+                + "isLetterboxEducationEnabled= " + isLetterboxEducationEnabled
                 + " isLetterboxDoubleTapEnabled= " + isLetterboxDoubleTapEnabled
                 + " topActivityEligibleForUserAspectRatioButton= "
                 + topActivityEligibleForUserAspectRatioButton
diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java
index ef6982e..4ac40a1 100644
--- a/core/java/android/app/DreamManager.java
+++ b/core/java/android/app/DreamManager.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 
+import android.annotation.FlaggedApi;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
@@ -30,6 +31,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.dreams.DreamService;
+import android.service.dreams.Flags;
 import android.service.dreams.IDreamManager;
 
 /**
@@ -217,4 +219,19 @@
         }
         return false;
     }
+
+    /**
+     * Sets whether the dream is obscured by something.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_DREAM_HANDLES_BEING_OBSCURED)
+    @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+    public void setDreamIsObscured(boolean isObscured) {
+        try {
+            mService.setDreamIsObscured(isObscured);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 97c2e43..2e38c06 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -60,6 +60,9 @@
 # ComponentCaller
 per-file ComponentCaller.java = file:COMPONENT_CALLER_OWNERS
 
+# DreamManager
+per-file DreamManager.java = file:/DREAM_MANAGER_OWNERS
+
 # GrammaticalInflectionManager
 per-file *GrammaticalInflection* = file:/services/core/java/com/android/server/grammaticalinflection/OWNERS
 per-file grammatical_inflection_manager.aconfig = file:/services/core/java/com/android/server/grammaticalinflection/OWNERS
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 57b5c13..3213b40 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -1041,6 +1041,7 @@
      */
     public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
         if (mService == null) {
+            Log.e(TAG, "Service wasn't initialized, appWidgetId=" + appWidgetId);
             return null;
         }
         try {
@@ -1048,6 +1049,9 @@
             if (info != null) {
                 // Converting complex to dp.
                 info.updateDimensions(mDisplayMetrics);
+            } else {
+                Log.e(TAG, "App widget provider info is null. PackageName=" + mPackageName
+                        + " appWidgetId-" + appWidgetId);
             }
             return info;
         } catch (RemoteException e) {
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index d340f3f..3f2ef84 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -732,6 +732,10 @@
 
     /**
      * Get statically configured sensor properties.
+     * @deprecated Generally unsafe to use, use
+     * {@link FaceManager#addAuthenticatorsRegisteredCallback} API instead.
+     * In most cases this method will work as expected, but during early boot up, it will be
+     * null/empty and there is no way for the caller to know when it's actual value is ready.
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 25bfb2a..2ded615 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -1189,6 +1189,10 @@
 
     /**
      * Get statically configured sensor properties.
+     * @deprecated Generally unsafe to use, use
+     * {@link FingerprintManager#addAuthenticatorsRegisteredCallback} API instead.
+     * In most cases this method will work as expected, but during early boot up, it will be
+     * null/empty and there is no way for the caller to know when it's actual value is ready.
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 5f6bdbf..38ab590 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -18,7 +18,7 @@
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.service.dreams.Flags.dreamHandlesConfirmKeys;
-import static android.service.dreams.Flags.dreamTracksFocus;
+import static android.service.dreams.Flags.dreamHandlesBeingObscured;
 
 import android.annotation.FlaggedApi;
 import android.annotation.IdRes;
@@ -571,15 +571,6 @@
     /** {@inheritDoc} */
     @Override
     public void onWindowFocusChanged(boolean hasFocus) {
-        if (!dreamTracksFocus()) {
-            return;
-        }
-
-        try {
-            mDreamManager.onDreamFocusChanged(hasFocus);
-        } catch (RemoteException ex) {
-            // system server died
-        }
     }
 
     /** {@inheritDoc} */
@@ -1737,7 +1728,7 @@
 
         @Override
         public void comeToFront() {
-            if (!dreamTracksFocus()) {
+            if (!dreamHandlesBeingObscured()) {
                 return;
             }
 
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 85f0368..cf98bfe0 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -48,5 +48,6 @@
     void setSystemDreamComponent(in ComponentName componentName);
     void registerDreamOverlayService(in ComponentName componentName);
     void startDreamActivity(in Intent intent);
-    void onDreamFocusChanged(in boolean hasFocus);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)")
+    oneway void setDreamIsObscured(in boolean isObscured);
 }
diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
index a42eaff..54d950c 100644
--- a/core/java/android/service/dreams/flags.aconfig
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -39,8 +39,11 @@
 }
 
 flag {
-  name: "dream_tracks_focus"
+  name: "dream_handles_being_obscured"
   namespace: "communal"
-  description: "This flag enables the ability for dreams to track whether or not they have focus"
-  bug: "331798001"
+  description: "This flag enables the ability for dreams to handle being obscured"
+  bug: "337302237"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
index 29a6db6..8237b20 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
@@ -105,6 +105,21 @@
     public static final String SERVICE_INTERFACE =
             "android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService";
 
+    // TODO(339594686): make API
+    /**
+     * @hide
+     */
+    public static final String REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY =
+            "register_model_update_callback";
+    /**
+     * @hide
+     */
+    public static final String MODEL_LOADED_BUNDLE_KEY = "model_loaded";
+    /**
+     * @hide
+     */
+    public static final String MODEL_UNLOADED_BUNDLE_KEY = "model_unloaded";
+
     private IRemoteStorageService mRemoteStorageService;
 
     /**
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index bf2fdda..acef609 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -330,7 +330,7 @@
         InputDeviceInfo info = InputDeviceInfo();
         info.initialize(keyboardId, 0, 0, InputDeviceIdentifier(),
                         "keyboard " + std::to_string(keyboardId), true, false,
-                        ui::ADISPLAY_ID_DEFAULT);
+                        ui::LogicalDisplayId::DEFAULT);
         info.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
         info.setKeyCharacterMap(*charMap);
 
diff --git a/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml b/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml
index 7c45c20..c692967 100644
--- a/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml
@@ -22,11 +22,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7v13H7L20,7 M22,2L2,22h20V2L22,2z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 1 bar. move to higher ground. -->
         <path
             android:name="ic_signal_cellular_1_4_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H11 V20 H6 z" />
+            android:pathData="M0,0 H11 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml
index 02b646d..b01c269 100644
--- a/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml
@@ -22,11 +22,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 1 bar. might have to call you back. -->
         <path
             android:name="ic_signal_cellular_1_5_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H12 V20 H6 z" />
+            android:pathData="M0,0 H12 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml b/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml
index 514d169..982623d 100644
--- a/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml
@@ -22,11 +22,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7v13H7L20,7 M22,2L2,22h20V2L22,2z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 2 bars. 2 out of 4 ain't bad. -->
         <path
             android:name="ic_signal_cellular_2_4_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H14 V20 H6 z" />
+            android:pathData="M0,0 H14 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml
index a97f771..75daadd 100644
--- a/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml
@@ -23,11 +23,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 2 bars. hanging in there. -->
         <path
             android:name="ic_signal_cellular_2_5_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H14 V20 H6 z" />
+            android:pathData="M0,0 H14 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml b/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml
index 1bacf4a..4e4bea3 100644
--- a/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml
@@ -22,11 +22,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7v13H7L20,7 M22,2L2,22h20V2L22,2z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 3 bars. quite nice. -->
         <path
             android:name="ic_signal_cellular_3_4_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H17 V20 H6 z" />
+            android:pathData="M0,0 H17 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml
index 2789d3e..9a98c29 100644
--- a/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml
@@ -22,11 +22,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 3 bars. not great, not terrible. -->
         <path
             android:name="ic_signal_cellular_3_5_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H16 V20 H6 z" />
+            android:pathData="M0,0 H16 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml
index 8286dbb..2a37d01 100644
--- a/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml
@@ -22,11 +22,11 @@
     <path
         android:fillColor="@android:color/white"
         android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" />
-    <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+    <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
         <!-- 4 bars. extremely respectable. -->
         <path
             android:name="ic_signal_cellular_4_5_bar"
             android:fillColor="@android:color/white"
-            android:pathData="M6,0 H18 V20 H6 z" />
+            android:pathData="M0,0 H18 V24 H0 z" />
     </clip-path>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 37d39a7..5fa13ba 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1346,6 +1346,51 @@
         <attr name="materialColorTertiary" format="color"/>
         <!-- The error color for the app, intended to draw attention to error conditions. @hide -->
         <attr name="materialColorError" format="color"/>
+
+        <!-- System Custom Tokens-->
+        <!-- @hide -->
+        <attr name="customColorWidgetBackground" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorClockHour" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorClockMinute" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorClockSecond" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorThemeApp" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOnThemeApp" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorThemeAppRing" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOnThemeAppRing" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorBrandA" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorBrandB" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorBrandC" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorBrandD" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorUnderSurface" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorShadeActive" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOnShadeActive" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOnShadeActiveVariant" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorShadeInactive" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOnShadeInactive" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOnShadeInactiveVariant" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorShadeDisabled" format="color"/>
+        <!-- @hide -->
+        <attr name="customColorOverviewBackground" format="color"/>
+
     </declare-styleable>
 
     <!-- **************************************************************** -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index e671919..5e039b5 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -581,6 +581,51 @@
     <color name="system_on_tertiary_fixed">#FFFFFF</color>
     <color name="system_on_tertiary_fixed_variant">#FFFFFF</color>
 
+    <!--Colors used in Android system, from design system. These values can be overlaid at runtime
+     by OverlayManager RROs.-->
+    <color name="system_widget_background_light">#EEF0FF</color>
+    <color name="system_clock_hour_light">#1D2435</color>
+    <color name="system_clock_minute_light">#20386A</color>
+    <color name="system_clock_second_light">#000000</color>
+    <color name="system_theme_app_light">#2F4578</color>
+    <color name="system_on_theme_app_light">#D6DFFF</color>
+    <color name="system_theme_app_ring_light">#94AAE4</color>
+    <color name="system_on_theme_app_ring_light">#FDD7FA</color>
+    <color name="system_brand_a_light">#3A5084</color>
+    <color name="system_brand_b_light">#6E7488</color>
+    <color name="system_brand_c_light">#6076AC</color>
+    <color name="system_brand_d_light">#8C6D8C</color>
+    <color name="system_under_surface_light">#000000</color>
+    <color name="system_shade_active_light">#D9E2FF</color>
+    <color name="system_on_shade_active_light">#152E60</color>
+    <color name="system_on_shade_active_variant_light">#2F4578</color>
+    <color name="system_shade_inactive_light">#2F3036</color>
+    <color name="system_on_shade_inactive_light">#E1E2EC</color>
+    <color name="system_on_shade_inactive_variant_light">#C5C6D0</color>
+    <color name="system_shade_disabled_light">#0C0E13</color>
+    <color name="system_overview_background_light">#50525A</color>
+    <color name="system_widget_background_dark">#152E60</color>
+    <color name="system_clock_hour_dark">#9AA0B6</color>
+    <color name="system_clock_minute_dark">#D8E1FF</color>
+    <color name="system_clock_second_dark">#FFFFFF</color>
+    <color name="system_theme_app_dark">#D9E2FF</color>
+    <color name="system_on_theme_app_dark">#304679</color>
+    <color name="system_theme_app_ring_dark">#94AAE4</color>
+    <color name="system_on_theme_app_ring_dark">#E0BBDD</color>
+    <color name="system_brand_a_dark">#90A6DF</color>
+    <color name="system_brand_b_dark">#A4ABC1</color>
+    <color name="system_brand_c_dark">#7A90C8</color>
+    <color name="system_brand_d_dark">#A886A6</color>
+    <color name="system_under_surface_dark">#000000</color>
+    <color name="system_shade_active_dark">#D9E2FF</color>
+    <color name="system_on_shade_active_dark">#001945</color>
+    <color name="system_on_shade_active_variant_dark">#2F4578</color>
+    <color name="system_shade_inactive_dark">#2F3036</color>
+    <color name="system_on_shade_inactive_dark">#E1E2EC</color>
+    <color name="system_on_shade_inactive_variant_dark">#C5C6D0</color>
+    <color name="system_shade_disabled_dark">#0C0E13</color>
+    <color name="system_overview_background_dark">#C5C6D0</color>
+
     <!-- Accessibility shortcut icon background color -->
     <color name="accessibility_feature_background">#5F6368</color> <!-- Google grey 700 -->
     <color name="accessibility_magnification_background">#F50D60</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 37771a2..5bd2033 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4717,6 +4717,13 @@
     <!-- The component name for the default system on-device sandboxed inference service. -->
     <string name="config_defaultOnDeviceSandboxedInferenceService" translatable="false"></string>
 
+    <!-- The broadcast intent name for notifying when the on-device model is loading  -->
+    <string name="config_onDeviceIntelligenceModelLoadedBroadcastKey" translatable="false"></string>
+
+    <!-- The broadcast intent name for notifying when the on-device model has been unloaded  -->
+    <string name="config_onDeviceIntelligenceModelUnloadedBroadcastKey" translatable="false"></string>
+
+
     <!-- Component name that accepts ACTION_SEND intents for requesting ambient context consent for
          wearable sensing. -->
     <string translatable="false" name="config_defaultWearableSensingConsentComponent"></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e5768e4..d004b05 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3943,6 +3943,8 @@
   <java-symbol type="string" name="config_defaultWearableSensingService" />
   <java-symbol type="string" name="config_defaultOnDeviceIntelligenceService" />
   <java-symbol type="string" name="config_defaultOnDeviceSandboxedInferenceService" />
+  <java-symbol type="string" name="config_onDeviceIntelligenceModelLoadedBroadcastKey" />
+  <java-symbol type="string" name="config_onDeviceIntelligenceModelUnloadedBroadcastKey" />
   <java-symbol type="string" name="config_retailDemoPackage" />
   <java-symbol type="string" name="config_retailDemoPackageSignature" />
 
@@ -5290,6 +5292,71 @@
   <java-symbol name="materialColorTertiary" type="attr"/>
   <java-symbol name="materialColorError" type="attr"/>
 
+  <java-symbol name="customColorWidgetBackground" type="attr"/>
+  <java-symbol name="customColorClockHour" type="attr"/>
+  <java-symbol name="customColorClockMinute" type="attr"/>
+  <java-symbol name="customColorClockSecond" type="attr"/>
+  <java-symbol name="customColorThemeApp" type="attr"/>
+  <java-symbol name="customColorOnThemeApp" type="attr"/>
+  <java-symbol name="customColorThemeAppRing" type="attr"/>
+  <java-symbol name="customColorOnThemeAppRing" type="attr"/>
+  <java-symbol name="customColorBrandA" type="attr"/>
+  <java-symbol name="customColorBrandB" type="attr"/>
+  <java-symbol name="customColorBrandC" type="attr"/>
+  <java-symbol name="customColorBrandD" type="attr"/>
+  <java-symbol name="customColorUnderSurface" type="attr"/>
+  <java-symbol name="customColorShadeActive" type="attr"/>
+  <java-symbol name="customColorOnShadeActive" type="attr"/>
+  <java-symbol name="customColorOnShadeActiveVariant" type="attr"/>
+  <java-symbol name="customColorShadeInactive" type="attr"/>
+  <java-symbol name="customColorOnShadeInactive" type="attr"/>
+  <java-symbol name="customColorOnShadeInactiveVariant" type="attr"/>
+  <java-symbol name="customColorShadeDisabled" type="attr"/>
+  <java-symbol name="customColorOverviewBackground" type="attr"/>
+
+  <java-symbol name="system_widget_background_light" type="color"/>
+  <java-symbol name="system_clock_hour_light" type="color"/>
+  <java-symbol name="system_clock_minute_light" type="color"/>
+  <java-symbol name="system_clock_second_light" type="color"/>
+  <java-symbol name="system_theme_app_light" type="color"/>
+  <java-symbol name="system_on_theme_app_light" type="color"/>
+  <java-symbol name="system_theme_app_ring_light" type="color"/>
+  <java-symbol name="system_on_theme_app_ring_light" type="color"/>
+  <java-symbol name="system_brand_a_light" type="color"/>
+  <java-symbol name="system_brand_b_light" type="color"/>
+  <java-symbol name="system_brand_c_light" type="color"/>
+  <java-symbol name="system_brand_d_light" type="color"/>
+  <java-symbol name="system_under_surface_light" type="color"/>
+  <java-symbol name="system_shade_active_light" type="color"/>
+  <java-symbol name="system_on_shade_active_light" type="color"/>
+  <java-symbol name="system_on_shade_active_variant_light" type="color"/>
+  <java-symbol name="system_shade_inactive_light" type="color"/>
+  <java-symbol name="system_on_shade_inactive_light" type="color"/>
+  <java-symbol name="system_on_shade_inactive_variant_light" type="color"/>
+  <java-symbol name="system_shade_disabled_light" type="color"/>
+  <java-symbol name="system_overview_background_light" type="color"/>
+  <java-symbol name="system_widget_background_dark" type="color"/>
+  <java-symbol name="system_clock_hour_dark" type="color"/>
+  <java-symbol name="system_clock_minute_dark" type="color"/>
+  <java-symbol name="system_clock_second_dark" type="color"/>
+  <java-symbol name="system_theme_app_dark" type="color"/>
+  <java-symbol name="system_on_theme_app_dark" type="color"/>
+  <java-symbol name="system_theme_app_ring_dark" type="color"/>
+  <java-symbol name="system_on_theme_app_ring_dark" type="color"/>
+  <java-symbol name="system_brand_a_dark" type="color"/>
+  <java-symbol name="system_brand_b_dark" type="color"/>
+  <java-symbol name="system_brand_c_dark" type="color"/>
+  <java-symbol name="system_brand_d_dark" type="color"/>
+  <java-symbol name="system_under_surface_dark" type="color"/>
+  <java-symbol name="system_shade_active_dark" type="color"/>
+  <java-symbol name="system_on_shade_active_dark" type="color"/>
+  <java-symbol name="system_on_shade_active_variant_dark" type="color"/>
+  <java-symbol name="system_shade_inactive_dark" type="color"/>
+  <java-symbol name="system_on_shade_inactive_dark" type="color"/>
+  <java-symbol name="system_on_shade_inactive_variant_dark" type="color"/>
+  <java-symbol name="system_shade_disabled_dark" type="color"/>
+  <java-symbol name="system_overview_background_dark" type="color"/>
+
   <java-symbol type="attr" name="actionModeUndoDrawable" />
   <java-symbol type="attr" name="actionModeRedoDrawable" />
 
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index ee19144..24d4938 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -284,6 +284,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -380,6 +402,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar.  This theme
@@ -475,6 +519,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -572,6 +638,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -668,6 +756,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -772,6 +882,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -867,6 +999,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -961,6 +1115,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -1056,6 +1232,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -1167,6 +1365,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -1263,6 +1483,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -1357,6 +1599,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -1453,6 +1717,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1548,6 +1834,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1643,6 +1951,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -1738,6 +2068,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -1833,6 +2185,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -1928,6 +2302,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Theme for the dialog shown when an app crashes or ANRs. -->
@@ -2028,6 +2424,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -2121,6 +2539,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
@@ -2352,6 +2792,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
@@ -2447,6 +2909,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
@@ -2541,6 +3025,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
@@ -2636,6 +3142,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
@@ -2733,6 +3261,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
@@ -2829,6 +3379,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -2931,6 +3503,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
@@ -3029,6 +3623,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
@@ -3126,6 +3742,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
@@ -3224,6 +3862,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -3303,6 +3963,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -3382,6 +4064,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -3480,6 +4184,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault light theme for a window without an action bar that will be displayed either
@@ -3579,6 +4305,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault light theme for a presentation window on a secondary display. -->
@@ -3676,6 +4424,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault light theme for panel windows. This removes all extraneous window
@@ -3772,6 +4542,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -3867,6 +4659,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -3962,6 +4776,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -4055,6 +4891,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- DeviceDefault theme for a window that should look like the Settings app.  -->
@@ -4156,6 +5014,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.SystemUI" parent="Theme.DeviceDefault.Light">
@@ -4238,6 +5118,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.SystemUI.Dialog" parent="Theme.DeviceDefault.Light.Dialog">
@@ -4312,6 +5214,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
@@ -4407,6 +5331,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.DialogBase" parent="Theme.Material.Light.BaseDialog">
@@ -4486,6 +5432,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.DeviceDefault.Settings.DialogBase">
@@ -4605,6 +5573,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -4702,6 +5692,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
@@ -4825,6 +5837,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <style name="ThemeOverlay.DeviceDefault.Accent.Light">
@@ -4878,6 +5912,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. -->
@@ -4935,6 +5991,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
 
     <style name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" parent="Theme.DeviceDefault.NoActionBar.Fullscreen">
@@ -4988,6 +6066,28 @@
         <item name="materialColorSecondary">@color/system_secondary_light</item>
         <item name="materialColorTertiary">@color/system_tertiary_light</item>
         <item name="materialColorError">@color/system_error_light</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+        <item name="customColorClockHour">@color/system_clock_hour_light</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+        <item name="customColorClockSecond">@color/system_clock_second_light</item>
+        <item name="customColorThemeApp">@color/system_theme_app_light</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+        <item name="customColorBrandA">@color/system_brand_a_light</item>
+        <item name="customColorBrandB">@color/system_brand_b_light</item>
+        <item name="customColorBrandC">@color/system_brand_c_light</item>
+        <item name="customColorBrandD">@color/system_brand_d_light</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+        <item name="customColorShadeActive">@color/system_shade_active_light</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
     </style>
 
     <style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification">
@@ -5052,6 +6152,28 @@
         <item name="materialColorSecondary">@color/system_secondary_dark</item>
         <item name="materialColorTertiary">@color/system_tertiary_dark</item>
         <item name="materialColorError">@color/system_error_dark</item>
+
+        <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+        <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+        <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+        <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+        <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+        <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+        <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+        <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+        <item name="customColorBrandA">@color/system_brand_a_dark</item>
+        <item name="customColorBrandB">@color/system_brand_b_dark</item>
+        <item name="customColorBrandC">@color/system_brand_c_dark</item>
+        <item name="customColorBrandD">@color/system_brand_d_dark</item>
+        <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+        <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+        <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+        <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+        <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+        <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+        <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+        <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
     </style>
     <style name="Theme.DeviceDefault.AutofillHalfScreenDialogList" parent="Theme.DeviceDefault.DayNight">
         <item name="colorListDivider">@color/list_divider_opacity_device_default_light</item>
diff --git a/core/tests/coretests/src/android/net/OWNERS b/core/tests/coretests/src/android/net/OWNERS
index a779c00..beb77dc 100644
--- a/core/tests/coretests/src/android/net/OWNERS
+++ b/core/tests/coretests/src/android/net/OWNERS
@@ -1,4 +1,5 @@
 include /services/core/java/com/android/server/net/OWNERS
 
-per-file SSL*,Uri*,Url* = prb@google.com,oth@google.com,narayan@google.com,ngeoffray@google.com
+per-file SSL*,Url* = prb@google.com,oth@google.com,narayan@google.com,ngeoffray@google.com
 per-file SntpClient* = file:/services/core/java/com/android/server/timedetector/OWNERS
+per-file Uri* = varunshah@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 163a896..5600664 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -646,7 +646,7 @@
 
     private void tryDispatchOnBackCancelled(IOnBackInvokedCallback callback) {
         if (!mOnBackStartDispatched) {
-            Log.e(TAG, "Skipping dispatching onBackCancelled. Start was never dispatched.");
+            Log.d(TAG, "Skipping dispatching onBackCancelled. Start was never dispatched.");
             return;
         }
         if (callback == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index 037b1ec..c988c2f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.wm.shell.back
 
 import android.animation.Animator
@@ -34,6 +35,7 @@
 import android.view.SurfaceControl
 import android.view.animation.DecelerateInterpolator
 import android.view.animation.Interpolator
+import android.view.animation.Transformation
 import android.window.BackEvent
 import android.window.BackMotionEvent
 import android.window.BackNavigationInfo
@@ -46,52 +48,45 @@
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.animation.Interpolators
 import com.android.wm.shell.protolog.ShellProtoLogGroup
-import com.android.wm.shell.shared.annotations.ShellMainThread
-import javax.inject.Inject
 import kotlin.math.abs
 import kotlin.math.max
 import kotlin.math.min
 
-/** Class that defines cross-activity animation.  */
-@ShellMainThread
-class CrossActivityBackAnimation @Inject constructor(
+abstract class CrossActivityBackAnimation(
     private val context: Context,
     private val background: BackAnimationBackground,
-    private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+    private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+    protected val transaction: SurfaceControl.Transaction,
+    private val choreographer: Choreographer
 ) : ShellBackAnimation() {
 
-    private val startClosingRect = RectF()
-    private val targetClosingRect = RectF()
-    private val currentClosingRect = RectF()
+    protected val startClosingRect = RectF()
+    protected val targetClosingRect = RectF()
+    protected val currentClosingRect = RectF()
 
-    private val startEnteringRect = RectF()
-    private val targetEnteringRect = RectF()
-    private val currentEnteringRect = RectF()
+    protected val startEnteringRect = RectF()
+    protected val targetEnteringRect = RectF()
+    protected val currentEnteringRect = RectF()
 
-    private val backAnimRect = Rect()
+    protected val backAnimRect = Rect()
     private val cropRect = Rect()
 
     private var cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
 
-    private val backAnimationRunner = BackAnimationRunner(
-        Callback(), Runner(), context, Cuj.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY
-    )
+    private val backAnimationRunner =
+        BackAnimationRunner(Callback(), Runner(), context, Cuj.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY)
     private val initialTouchPos = PointF()
     private val transformMatrix = Matrix()
     private val tmpFloat9 = FloatArray(9)
-    private var enteringTarget: RemoteAnimationTarget? = null
-    private var closingTarget: RemoteAnimationTarget? = null
-    private val transaction = SurfaceControl.Transaction()
+    protected var enteringTarget: RemoteAnimationTarget? = null
+    protected var closingTarget: RemoteAnimationTarget? = null
     private var triggerBack = false
     private var finishCallback: IRemoteAnimationFinishedCallback? = null
     private val progressAnimator = BackProgressAnimator()
     private val displayBoundsMargin =
         context.resources.getDimension(R.dimen.cross_task_back_vertical_margin)
-    private val enteringStartOffset =
-        context.resources.getDimension(R.dimen.cross_activity_back_entering_start_offset)
 
     private val gestureInterpolator = Interpolators.BACK_GESTURE
-    private val postCommitInterpolator = Interpolators.FAST_OUT_SLOW_IN
     private val verticalMoveInterpolator: Interpolator = DecelerateInterpolator()
 
     private var scrimLayer: SurfaceControl? = null
@@ -103,13 +98,42 @@
     private var rightLetterboxLayer: SurfaceControl? = null
     private var letterboxColor: Int = 0
 
+    /** Background color to be used during the animation, also see [getBackgroundColor] */
+    protected var customizedBackgroundColor = 0
+
+    /**
+     * Whether the entering target should be shifted vertically with the user gesture in pre-commit
+     */
+    abstract val allowEnteringYShift: Boolean
+
+    /**
+     * Subclasses must set the [startEnteringRect] and [targetEnteringRect] to define the movement
+     * of the enteringTarget during pre-commit phase.
+     */
+    abstract fun preparePreCommitEnteringRectMovement()
+
+    /**
+     * Returns a base transformation to apply to the entering target during pre-commit. The system
+     * will apply the default animation on top of it.
+     */
+    protected open fun getPreCommitEnteringBaseTransformation(progress: Float): Transformation? =
+        null
+
     override fun onConfigurationChanged(newConfiguration: Configuration) {
         cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
     }
 
     override fun getRunner() = backAnimationRunner
 
-    private fun startBackAnimation(backMotionEvent: BackMotionEvent) {
+    private fun getBackgroundColor(): Int =
+        when {
+            customizedBackgroundColor != 0 -> customizedBackgroundColor
+            isLetterboxed -> letterboxColor
+            enteringTarget != null -> enteringTarget!!.taskInfo.taskDescription!!.backgroundColor
+            else -> 0
+        }
+
+    protected open fun startBackAnimation(backMotionEvent: BackMotionEvent) {
         if (enteringTarget == null || closingTarget == null) {
             ProtoLog.d(
                 ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW,
@@ -122,8 +146,8 @@
 
         transaction.setAnimationTransaction()
         isLetterboxed = closingTarget!!.taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed
-        enteringHasSameLetterbox = isLetterboxed &&
-                closingTarget!!.localBounds.equals(enteringTarget!!.localBounds)
+        enteringHasSameLetterbox =
+            isLetterboxed && closingTarget!!.localBounds.equals(enteringTarget!!.localBounds)
 
         if (isLetterboxed && !enteringHasSameLetterbox) {
             // Play animation with letterboxes, if closing and entering target have mismatching
@@ -143,32 +167,27 @@
         targetClosingRect.scaleCentered(MAX_SCALE)
         if (backMotionEvent.swipeEdge != BackEvent.EDGE_RIGHT) {
             targetClosingRect.offset(
-                startClosingRect.right - targetClosingRect.right - displayBoundsMargin, 0f
+                startClosingRect.right - targetClosingRect.right - displayBoundsMargin,
+                0f
             )
         }
 
-        // the entering target starts 96dp to the left of the screen edge...
-        startEnteringRect.set(startClosingRect)
-        startEnteringRect.offset(-enteringStartOffset, 0f)
+        preparePreCommitEnteringRectMovement()
 
-        // ...and gets scaled in sync with the closing target
-        targetEnteringRect.set(startEnteringRect)
-        targetEnteringRect.scaleCentered(MAX_SCALE)
-
-        // Draw background with task background color (or letterbox color).
-        val backgroundColor = if (isLetterboxed) {
-            letterboxColor
-        } else {
-            enteringTarget!!.taskInfo.taskDescription!!.backgroundColor
-        }
         background.ensureBackground(
-            closingTarget!!.windowConfiguration.bounds, backgroundColor, transaction
+            closingTarget!!.windowConfiguration.bounds,
+            getBackgroundColor(),
+            transaction
         )
         ensureScrimLayer()
         if (isLetterboxed && enteringHasSameLetterbox) {
             // crop left and right letterboxes
-            cropRect.set(closingTarget!!.localBounds.left, 0, closingTarget!!.localBounds.right,
-                    closingTarget!!.windowConfiguration.bounds.height())
+            cropRect.set(
+                closingTarget!!.localBounds.left,
+                0,
+                closingTarget!!.localBounds.right,
+                closingTarget!!.windowConfiguration.bounds.height()
+            )
             // and add fake letterbox square surfaces instead
             ensureLetterboxes()
         } else {
@@ -185,8 +204,14 @@
         currentClosingRect.offset(0f, yOffset)
         applyTransform(closingTarget?.leash, currentClosingRect, 1f)
         currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress)
-        currentEnteringRect.offset(0f, yOffset)
-        applyTransform(enteringTarget?.leash, currentEnteringRect, 1f)
+        if (allowEnteringYShift) currentEnteringRect.offset(0f, yOffset)
+        val enteringTransformation = getPreCommitEnteringBaseTransformation(progress)
+        applyTransform(
+            enteringTarget?.leash,
+            currentEnteringRect,
+            enteringTransformation?.alpha ?: 1f,
+            enteringTransformation
+        )
         applyTransaction()
     }
 
@@ -199,30 +224,25 @@
         val deltaYRatio = min(screenHeight / 2f, abs(rawYDelta)) / (screenHeight / 2f)
         val interpolatedYRatio: Float = verticalMoveInterpolator.getInterpolation(deltaYRatio)
         // limit y-shift so surface never passes 8dp screen margin
-        val deltaY = yDirection * interpolatedYRatio * max(
-            0f, (screenHeight - centeredRect.height()) / 2f - displayBoundsMargin
-        )
+        val deltaY =
+            max(0f, (screenHeight - centeredRect.height()) / 2f - displayBoundsMargin) *
+                interpolatedYRatio *
+                yDirection
         return deltaY
     }
 
-    private fun onGestureCommitted() {
-        if (closingTarget?.leash == null || enteringTarget?.leash == null ||
-                !enteringTarget!!.leash.isValid || !closingTarget!!.leash.isValid
+    protected open fun onGestureCommitted() {
+        if (
+            closingTarget?.leash == null ||
+                enteringTarget?.leash == null ||
+                !enteringTarget!!.leash.isValid ||
+                !closingTarget!!.leash.isValid
         ) {
             finishAnimation()
             return
         }
 
-        // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current
-        // coordinate of the gesture driven phase. Let's update the start and target rects and kick
-        // off the animator
-        startClosingRect.set(currentClosingRect)
-        startEnteringRect.set(currentEnteringRect)
-        targetEnteringRect.set(backAnimRect)
-        targetClosingRect.set(backAnimRect)
-        targetClosingRect.offset(currentClosingRect.left + enteringStartOffset, 0f)
-
-        val valueAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(POST_ANIMATION_DURATION)
+        val valueAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(POST_COMMIT_DURATION)
         valueAnimator.addUpdateListener { animation: ValueAnimator ->
             val progress = animation.animatedFraction
             onPostCommitProgress(progress)
@@ -230,27 +250,22 @@
                 background.resetStatusBarCustomization()
             }
         }
-        valueAnimator.addListener(object : AnimatorListenerAdapter() {
-            override fun onAnimationEnd(animation: Animator) {
-                background.resetStatusBarCustomization()
-                finishAnimation()
+        valueAnimator.addListener(
+            object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator) {
+                    background.resetStatusBarCustomization()
+                    finishAnimation()
+                }
             }
-        })
+        )
         valueAnimator.start()
     }
 
-    private fun onPostCommitProgress(linearProgress: Float) {
-        val closingAlpha = max(1f - linearProgress * 2, 0f)
-        val progress = postCommitInterpolator.getInterpolation(linearProgress)
+    protected open fun onPostCommitProgress(linearProgress: Float) {
         scrimLayer?.let { transaction.setAlpha(it, maxScrimAlpha * (1f - linearProgress)) }
-        currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress)
-        applyTransform(closingTarget?.leash, currentClosingRect, closingAlpha)
-        currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress)
-        applyTransform(enteringTarget?.leash, currentEnteringRect, 1f)
-        applyTransaction()
     }
 
-    private fun finishAnimation() {
+    protected open fun finishAnimation() {
         enteringTarget?.let {
             if (it.leash != null && it.leash.isValid) {
                 transaction.setCornerRadius(it.leash, 0f)
@@ -278,47 +293,56 @@
         enteringHasSameLetterbox = false
     }
 
-    private fun applyTransform(leash: SurfaceControl?, rect: RectF, alpha: Float) {
+    protected fun applyTransform(
+        leash: SurfaceControl?,
+        rect: RectF,
+        alpha: Float,
+        baseTransformation: Transformation? = null
+    ) {
         if (leash == null || !leash.isValid) return
         val scale = rect.width() / backAnimRect.width()
-        transformMatrix.reset()
-        val scalePivotX = if (isLetterboxed && enteringHasSameLetterbox) {
-            closingTarget!!.localBounds.left.toFloat()
-        } else {
-            0f
-        }
-        transformMatrix.setScale(scale, scale, scalePivotX, 0f)
-        transformMatrix.postTranslate(rect.left, rect.top)
-        transaction.setAlpha(leash, alpha)
-            .setMatrix(leash, transformMatrix, tmpFloat9)
+        val matrix = baseTransformation?.matrix ?: transformMatrix.apply { reset() }
+        val scalePivotX =
+            if (isLetterboxed && enteringHasSameLetterbox) {
+                closingTarget!!.localBounds.left.toFloat()
+            } else {
+                0f
+            }
+        matrix.postScale(scale, scale, scalePivotX, 0f)
+        matrix.postTranslate(rect.left, rect.top)
+        transaction
+            .setAlpha(leash, keepMinimumAlpha(alpha))
+            .setMatrix(leash, matrix, tmpFloat9)
             .setCrop(leash, cropRect)
             .setCornerRadius(leash, cornerRadius)
     }
 
-    private fun applyTransaction() {
-        transaction.setFrameTimelineVsync(Choreographer.getInstance().vsyncId)
+    protected fun applyTransaction() {
+        transaction.setFrameTimelineVsync(choreographer.vsyncId)
         transaction.apply()
     }
 
     private fun ensureScrimLayer() {
         if (scrimLayer != null) return
         val isDarkTheme: Boolean = isDarkMode(context)
-        val scrimBuilder = SurfaceControl.Builder()
-            .setName("Cross-Activity back animation scrim")
-            .setCallsite("CrossActivityBackAnimation")
-            .setColorLayer()
-            .setOpaque(false)
-            .setHidden(false)
+        val scrimBuilder =
+            SurfaceControl.Builder()
+                .setName("Cross-Activity back animation scrim")
+                .setCallsite("CrossActivityBackAnimation")
+                .setColorLayer()
+                .setOpaque(false)
+                .setHidden(false)
 
         rootTaskDisplayAreaOrganizer.attachToDisplayArea(Display.DEFAULT_DISPLAY, scrimBuilder)
         scrimLayer = scrimBuilder.build()
         val colorComponents = floatArrayOf(0f, 0f, 0f)
         maxScrimAlpha = if (isDarkTheme) MAX_SCRIM_ALPHA_DARK else MAX_SCRIM_ALPHA_LIGHT
-        val scrimCrop = if (isLetterboxed) {
-            closingTarget!!.windowConfiguration.bounds
-        } else {
-            closingTarget!!.localBounds
-        }
+        val scrimCrop =
+            if (isLetterboxed) {
+                closingTarget!!.windowConfiguration.bounds
+            } else {
+                closingTarget!!.localBounds
+            }
         transaction
             .setColor(scrimLayer, colorComponents)
             .setAlpha(scrimLayer!!, maxScrimAlpha)
@@ -339,21 +363,34 @@
     private fun ensureLetterboxes() {
         closingTarget?.let { t ->
             if (t.localBounds.left != 0 && leftLetterboxLayer == null) {
-                val bounds = Rect(0, t.windowConfiguration.bounds.top, t.localBounds.left,
-                        t.windowConfiguration.bounds.bottom)
+                val bounds =
+                    Rect(
+                        0,
+                        t.windowConfiguration.bounds.top,
+                        t.localBounds.left,
+                        t.windowConfiguration.bounds.bottom
+                    )
                 leftLetterboxLayer = ensureLetterbox(bounds)
             }
-            if (t.localBounds.right != t.windowConfiguration.bounds.right &&
-                    rightLetterboxLayer == null) {
-                val bounds = Rect(t.localBounds.right, t.windowConfiguration.bounds.top,
-                        t.windowConfiguration.bounds.right, t.windowConfiguration.bounds.bottom)
+            if (
+                t.localBounds.right != t.windowConfiguration.bounds.right &&
+                    rightLetterboxLayer == null
+            ) {
+                val bounds =
+                    Rect(
+                        t.localBounds.right,
+                        t.windowConfiguration.bounds.top,
+                        t.windowConfiguration.bounds.right,
+                        t.windowConfiguration.bounds.bottom
+                    )
                 rightLetterboxLayer = ensureLetterbox(bounds)
             }
         }
     }
 
     private fun ensureLetterbox(bounds: Rect): SurfaceControl {
-        val letterboxBuilder = SurfaceControl.Builder()
+        val letterboxBuilder =
+            SurfaceControl.Builder()
                 .setName("Cross-Activity back animation letterbox")
                 .setCallsite("CrossActivityBackAnimation")
                 .setColorLayer()
@@ -362,13 +399,17 @@
 
         rootTaskDisplayAreaOrganizer.attachToDisplayArea(Display.DEFAULT_DISPLAY, letterboxBuilder)
         val layer = letterboxBuilder.build()
-        val colorComponents = floatArrayOf(Color.red(letterboxColor) / 255f,
-                Color.green(letterboxColor) / 255f, Color.blue(letterboxColor) / 255f)
+        val colorComponents =
+            floatArrayOf(
+                Color.red(letterboxColor) / 255f,
+                Color.green(letterboxColor) / 255f,
+                Color.blue(letterboxColor) / 255f
+            )
         transaction
-                .setColor(layer, colorComponents)
-                .setCrop(layer, bounds)
-                .setRelativeLayer(layer, closingTarget!!.leash, 1)
-                .show(layer)
+            .setColor(layer, colorComponents)
+            .setCrop(layer, bounds)
+            .setRelativeLayer(layer, closingTarget!!.leash, 1)
+            .show(layer)
         return layer
     }
 
@@ -389,8 +430,8 @@
     }
 
     override fun prepareNextAnimation(
-            animationInfo: BackNavigationInfo.CustomAnimationInfo?,
-            letterboxColor: Int
+        animationInfo: BackNavigationInfo.CustomAnimationInfo?,
+        letterboxColor: Int
     ): Boolean {
         this.letterboxColor = letterboxColor
         return false
@@ -415,9 +456,7 @@
         }
 
         override fun onBackCancelled() {
-            progressAnimator.onBackCancelled {
-                finishAnimation()
-            }
+            progressAnimator.onBackCancelled { finishAnimation() }
         }
 
         override fun onBackInvoked() {
@@ -435,7 +474,8 @@
             finishedCallback: IRemoteAnimationFinishedCallback
         ) {
             ProtoLog.d(
-                ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, "Start back to activity animation."
+                ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW,
+                "Start back to activity animation."
             )
             for (a in apps) {
                 when (a.mode) {
@@ -452,23 +492,25 @@
     }
 
     companion object {
-        /** Max scale of the entering/closing window.*/
-        private const val MAX_SCALE = 0.9f
-
-        /** Duration of post animation after gesture committed.  */
-        private const val POST_ANIMATION_DURATION = 300L
-
+        /** Max scale of the closing window. */
+        internal const val MAX_SCALE = 0.9f
         private const val MAX_SCRIM_ALPHA_DARK = 0.8f
         private const val MAX_SCRIM_ALPHA_LIGHT = 0.2f
+        private const val POST_COMMIT_DURATION = 300L
     }
 }
 
+// The target will loose focus when alpha == 0, so keep a minimum value for it.
+private fun keepMinimumAlpha(transAlpha: Float): Float {
+    return max(transAlpha.toDouble(), 0.005).toFloat()
+}
+
 private fun isDarkMode(context: Context): Boolean {
     return context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
-            Configuration.UI_MODE_NIGHT_YES
+        Configuration.UI_MODE_NIGHT_YES
 }
 
-private fun RectF.setInterpolatedRectF(start: RectF, target: RectF, progress: Float) {
+internal fun RectF.setInterpolatedRectF(start: RectF, target: RectF, progress: Float) {
     require(!(progress < 0 || progress > 1)) { "Progress value must be between 0 and 1" }
     left = start.left + (target.left - start.left) * progress
     top = start.top + (target.top - start.top) * progress
@@ -476,7 +518,7 @@
     bottom = start.bottom + (target.bottom - start.bottom) * progress
 }
 
-private fun RectF.scaleCentered(
+internal fun RectF.scaleCentered(
     scale: Float,
     pivotX: Float = left + width() / 2,
     pivotY: Float = top + height() / 2
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
new file mode 100644
index 0000000..e6ec2b4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.back
+
+import android.content.Context
+import android.graphics.Rect
+import android.graphics.RectF
+import android.util.MathUtils
+import android.view.Choreographer
+import android.view.SurfaceControl
+import android.view.animation.Animation
+import android.view.animation.Transformation
+import android.window.BackMotionEvent
+import android.window.BackNavigationInfo
+import com.android.internal.R
+import com.android.internal.policy.TransitionAnimation
+import com.android.internal.protolog.common.ProtoLog
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import javax.inject.Inject
+
+/** Class that handles customized predictive cross activity back animations. */
+@ShellMainThread
+class CustomCrossActivityBackAnimation(
+    context: Context,
+    background: BackAnimationBackground,
+    rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+    transaction: SurfaceControl.Transaction,
+    choreographer: Choreographer,
+    private val customAnimationLoader: CustomAnimationLoader
+) :
+    CrossActivityBackAnimation(
+        context,
+        background,
+        rootTaskDisplayAreaOrganizer,
+        transaction,
+        choreographer
+    ) {
+
+    private var enterAnimation: Animation? = null
+    private var closeAnimation: Animation? = null
+    private val transformation = Transformation()
+    private var gestureProgress = 0f
+
+    override val allowEnteringYShift = false
+
+    @Inject
+    constructor(
+        context: Context,
+        background: BackAnimationBackground,
+        rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+    ) : this(
+        context,
+        background,
+        rootTaskDisplayAreaOrganizer,
+        SurfaceControl.Transaction(),
+        Choreographer.getInstance(),
+        CustomAnimationLoader(
+            TransitionAnimation(context, false /* debug */, "CustomCrossActivityBackAnimation")
+        )
+    )
+
+    override fun preparePreCommitEnteringRectMovement() {
+        // No movement for the entering rect
+        startEnteringRect.set(startClosingRect)
+        targetEnteringRect.set(startClosingRect)
+    }
+
+    override fun getPreCommitEnteringBaseTransformation(progress: Float): Transformation {
+        gestureProgress = progress
+        transformation.clear()
+        enterAnimation!!.getTransformationAt(progress * PRE_COMMIT_MAX_PROGRESS, transformation)
+        return transformation
+    }
+
+    override fun startBackAnimation(backMotionEvent: BackMotionEvent) {
+        super.startBackAnimation(backMotionEvent)
+        if (
+            closeAnimation == null ||
+                enterAnimation == null ||
+                closingTarget == null ||
+                enteringTarget == null
+        ) {
+            ProtoLog.d(
+                ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW,
+                "Enter animation or close animation is null."
+            )
+            return
+        }
+        initializeAnimation(closeAnimation!!, closingTarget!!.localBounds)
+        initializeAnimation(enterAnimation!!, enteringTarget!!.localBounds)
+    }
+
+    override fun onPostCommitProgress(linearProgress: Float) {
+        super.onPostCommitProgress(linearProgress)
+        if (closingTarget == null || enteringTarget == null) return
+
+        // TODO: Should we use the duration from the custom xml spec for the post-commit animation?
+        applyTransform(closingTarget!!.leash, currentClosingRect, linearProgress, closeAnimation!!)
+        val enteringProgress =
+            MathUtils.lerp(gestureProgress * PRE_COMMIT_MAX_PROGRESS, 1f, linearProgress)
+        applyTransform(
+            enteringTarget!!.leash,
+            currentEnteringRect,
+            enteringProgress,
+            enterAnimation!!
+        )
+        applyTransaction()
+    }
+
+    private fun applyTransform(
+        leash: SurfaceControl,
+        rect: RectF,
+        progress: Float,
+        animation: Animation
+    ) {
+        transformation.clear()
+        animation.getTransformationAt(progress, transformation)
+        applyTransform(leash, rect, transformation.alpha, transformation)
+    }
+
+    override fun finishAnimation() {
+        closeAnimation?.reset()
+        closeAnimation = null
+        enterAnimation?.reset()
+        enterAnimation = null
+        transformation.clear()
+        gestureProgress = 0f
+        super.finishAnimation()
+    }
+
+    /** Load customize animation before animation start. */
+    override fun prepareNextAnimation(
+        animationInfo: BackNavigationInfo.CustomAnimationInfo?,
+        letterboxColor: Int
+    ): Boolean {
+        super.prepareNextAnimation(animationInfo, letterboxColor)
+        if (animationInfo == null) return false
+        customAnimationLoader.loadAll(animationInfo)?.let { result ->
+            closeAnimation = result.closeAnimation
+            enterAnimation = result.enterAnimation
+            customizedBackgroundColor = result.backgroundColor
+            return true
+        }
+        return false
+    }
+
+    class AnimationLoadResult {
+        var closeAnimation: Animation? = null
+        var enterAnimation: Animation? = null
+        var backgroundColor = 0
+    }
+
+    companion object {
+        private const val PRE_COMMIT_MAX_PROGRESS = 0.2f
+    }
+}
+
+/** Helper class to load custom animation. */
+class CustomAnimationLoader(private val transitionAnimation: TransitionAnimation) {
+
+    /**
+     * Load both enter and exit animation for the close activity transition. Note that the result is
+     * only valid if the exit animation has set and loaded success. If the entering animation has
+     * not set(i.e. 0), here will load the default entering animation for it.
+     *
+     * @param animationInfo The information of customize animation, which can be set from
+     *   [Activity.overrideActivityTransition] and/or [LayoutParams.windowAnimations]
+     */
+    fun loadAll(
+        animationInfo: BackNavigationInfo.CustomAnimationInfo
+    ): CustomCrossActivityBackAnimation.AnimationLoadResult? {
+        if (animationInfo.packageName.isEmpty()) return null
+        val close = loadAnimation(animationInfo, false) ?: return null
+        val open = loadAnimation(animationInfo, true)
+        val result = CustomCrossActivityBackAnimation.AnimationLoadResult()
+        result.closeAnimation = close
+        result.enterAnimation = open
+        result.backgroundColor = animationInfo.customBackground
+        return result
+    }
+
+    /**
+     * Load enter or exit animation from CustomAnimationInfo
+     *
+     * @param animationInfo The information for customize animation.
+     * @param enterAnimation true when load for enter animation, false for exit animation.
+     * @return Loaded animation.
+     */
+    fun loadAnimation(
+        animationInfo: BackNavigationInfo.CustomAnimationInfo,
+        enterAnimation: Boolean
+    ): Animation? {
+        var a: Animation? = null
+        // Activity#overrideActivityTransition has higher priority than windowAnimations
+        // Try to get animation from Activity#overrideActivityTransition
+        if (
+            enterAnimation && animationInfo.customEnterAnim != 0 ||
+                !enterAnimation && animationInfo.customExitAnim != 0
+        ) {
+            a =
+                transitionAnimation.loadAppTransitionAnimation(
+                    animationInfo.packageName,
+                    if (enterAnimation) animationInfo.customEnterAnim
+                    else animationInfo.customExitAnim
+                )
+        } else if (animationInfo.windowAnimations != 0) {
+            // try to get animation from LayoutParams#windowAnimations
+            a =
+                transitionAnimation.loadAnimationAttr(
+                    animationInfo.packageName,
+                    animationInfo.windowAnimations,
+                    if (enterAnimation) R.styleable.WindowAnimation_activityCloseEnterAnimation
+                    else R.styleable.WindowAnimation_activityCloseExitAnimation,
+                    false /* translucent */
+                )
+        }
+        // Only allow to load default animation for opening target.
+        if (a == null && enterAnimation) {
+            a = loadDefaultOpenAnimation()
+        }
+        if (a != null) {
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, "custom animation loaded %s", a)
+        } else {
+            ProtoLog.e(ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, "No custom animation loaded")
+        }
+        return a
+    }
+
+    private fun loadDefaultOpenAnimation(): Animation? {
+        return transitionAnimation.loadDefaultAnimationAttr(
+            R.styleable.WindowAnimation_activityCloseEnterAnimation,
+            false /* translucent */
+        )
+    }
+}
+
+private fun initializeAnimation(animation: Animation, bounds: Rect) {
+    val width = bounds.width()
+    val height = bounds.height()
+    animation.initialize(width, height, width, height)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
deleted file mode 100644
index e27b40e..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.back;
-
-import static android.view.RemoteAnimationTarget.MODE_CLOSING;
-import static android.view.RemoteAnimationTarget.MODE_OPENING;
-
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.util.FloatProperty;
-import android.view.Choreographer;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.WindowManager.LayoutParams;
-import android.view.animation.Animation;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Transformation;
-import android.window.BackEvent;
-import android.window.BackMotionEvent;
-import android.window.BackNavigationInfo;
-import android.window.BackProgressAnimator;
-import android.window.IOnBackInvokedCallback;
-
-import com.android.internal.R;
-import com.android.internal.dynamicanimation.animation.SpringAnimation;
-import com.android.internal.dynamicanimation.animation.SpringForce;
-import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.internal.policy.TransitionAnimation;
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.shared.annotations.ShellMainThread;
-
-import javax.inject.Inject;
-
-/** Class that handle customized close activity transition animation. */
-@ShellMainThread
-public class CustomizeActivityAnimation extends ShellBackAnimation {
-    private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
-    private final BackAnimationRunner mBackAnimationRunner;
-    private float mCornerRadius;
-    private final SurfaceControl.Transaction mTransaction;
-    private final BackAnimationBackground mBackground;
-    private RemoteAnimationTarget mEnteringTarget;
-    private RemoteAnimationTarget mClosingTarget;
-    private IRemoteAnimationFinishedCallback mFinishCallback;
-    /** Duration of post animation after gesture committed. */
-    private static final int POST_ANIMATION_DURATION = 250;
-
-    private static final int SCALE_FACTOR = 1000;
-    private final SpringAnimation mProgressSpring;
-    private float mLatestProgress = 0.0f;
-
-    private static final float TARGET_COMMIT_PROGRESS = 0.5f;
-
-    private final float[] mTmpFloat9 = new float[9];
-    private final DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
-
-    final CustomAnimationLoader mCustomAnimationLoader;
-    private Animation mEnterAnimation;
-    private Animation mCloseAnimation;
-    private int mNextBackgroundColor;
-    final Transformation mTransformation = new Transformation();
-
-    private final Choreographer mChoreographer;
-    private final Context mContext;
-
-    @Inject
-    public CustomizeActivityAnimation(Context context, BackAnimationBackground background) {
-        this(context, background, new SurfaceControl.Transaction(), null);
-    }
-
-    CustomizeActivityAnimation(Context context, BackAnimationBackground background,
-            SurfaceControl.Transaction transaction, Choreographer choreographer) {
-        mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
-        mBackground = background;
-        mBackAnimationRunner = new BackAnimationRunner(
-                new Callback(), new Runner(), context, CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY);
-        mCustomAnimationLoader = new CustomAnimationLoader(context);
-
-        mProgressSpring = new SpringAnimation(this, ENTER_PROGRESS_PROP);
-        mProgressSpring.setSpring(new SpringForce()
-                .setStiffness(SpringForce.STIFFNESS_MEDIUM)
-                .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY));
-        mTransaction = transaction == null ? new SurfaceControl.Transaction() : transaction;
-        mChoreographer = choreographer != null ? choreographer : Choreographer.getInstance();
-        mContext = context;
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext);
-    }
-
-    private float getLatestProgress() {
-        return mLatestProgress * SCALE_FACTOR;
-    }
-    private void setLatestProgress(float value) {
-        mLatestProgress = value / SCALE_FACTOR;
-        applyTransformTransaction(mLatestProgress);
-    }
-
-    private static final FloatProperty<CustomizeActivityAnimation> ENTER_PROGRESS_PROP =
-            new FloatProperty<>("enter") {
-                @Override
-                public void setValue(CustomizeActivityAnimation anim, float value) {
-                    anim.setLatestProgress(value);
-                }
-
-                @Override
-                public Float get(CustomizeActivityAnimation object) {
-                    return object.getLatestProgress();
-                }
-            };
-
-    // The target will lose focus when alpha == 0, so keep a minimum value for it.
-    private static float keepMinimumAlpha(float transAlpha) {
-        return Math.max(transAlpha, 0.005f);
-    }
-
-    private static void initializeAnimation(Animation animation, Rect bounds) {
-        final int width = bounds.width();
-        final int height = bounds.height();
-        animation.initialize(width, height, width, height);
-    }
-
-    private void startBackAnimation() {
-        if (mEnteringTarget == null || mClosingTarget == null
-                || mCloseAnimation == null || mEnterAnimation == null) {
-            ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Entering target or closing target is null.");
-            return;
-        }
-        initializeAnimation(mCloseAnimation, mClosingTarget.localBounds);
-        initializeAnimation(mEnterAnimation, mEnteringTarget.localBounds);
-
-        // Draw background with task background color.
-        if (mEnteringTarget.taskInfo != null && mEnteringTarget.taskInfo.taskDescription != null) {
-            mBackground.ensureBackground(mClosingTarget.windowConfiguration.getBounds(),
-                    mNextBackgroundColor == Color.TRANSPARENT
-                            ? mEnteringTarget.taskInfo.taskDescription.getBackgroundColor()
-                            : mNextBackgroundColor,
-                    mTransaction);
-        }
-    }
-
-    private void applyTransformTransaction(float progress) {
-        if (mClosingTarget == null || mEnteringTarget == null) {
-            return;
-        }
-        applyTransform(mClosingTarget.leash, progress, mCloseAnimation);
-        applyTransform(mEnteringTarget.leash, progress, mEnterAnimation);
-        mTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId());
-        mTransaction.apply();
-    }
-
-    private void applyTransform(SurfaceControl leash, float progress, Animation animation) {
-        mTransformation.clear();
-        animation.getTransformationAt(progress, mTransformation);
-        mTransaction.setMatrix(leash, mTransformation.getMatrix(), mTmpFloat9);
-        mTransaction.setAlpha(leash, keepMinimumAlpha(mTransformation.getAlpha()));
-        mTransaction.setCornerRadius(leash, mCornerRadius);
-    }
-
-    void finishAnimation() {
-        if (mCloseAnimation != null) {
-            mCloseAnimation.reset();
-            mCloseAnimation = null;
-        }
-        if (mEnterAnimation != null) {
-            mEnterAnimation.reset();
-            mEnterAnimation = null;
-        }
-        if (mEnteringTarget != null) {
-            mEnteringTarget.leash.release();
-            mEnteringTarget = null;
-        }
-        if (mClosingTarget != null) {
-            mClosingTarget.leash.release();
-            mClosingTarget = null;
-        }
-        if (mBackground != null) {
-            mBackground.removeBackground(mTransaction);
-        }
-        mTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId());
-        mTransaction.apply();
-        mTransformation.clear();
-        mLatestProgress = 0;
-        mNextBackgroundColor = Color.TRANSPARENT;
-        if (mFinishCallback != null) {
-            try {
-                mFinishCallback.onAnimationFinished();
-            } catch (RemoteException e) {
-                e.printStackTrace();
-            }
-            mFinishCallback = null;
-        }
-        mProgressSpring.animateToFinalPosition(0);
-        mProgressSpring.skipToEnd();
-    }
-
-    void onGestureProgress(@NonNull BackEvent backEvent) {
-        if (mEnteringTarget == null || mClosingTarget == null
-                || mCloseAnimation == null || mEnterAnimation == null) {
-            return;
-        }
-
-        final float progress = backEvent.getProgress();
-
-        float springProgress = (progress > 0.1f
-                ? mapLinear(progress, 0.1f, 1f, TARGET_COMMIT_PROGRESS, 1f)
-                : mapLinear(progress, 0, 1f, 0f, TARGET_COMMIT_PROGRESS)) * SCALE_FACTOR;
-
-        mProgressSpring.animateToFinalPosition(springProgress);
-    }
-
-    static float mapLinear(float x, float a1, float a2, float b1, float b2) {
-        return b1 + (x - a1) * (b2 - b1) / (a2 - a1);
-    }
-
-    void onGestureCommitted() {
-        if (mEnteringTarget == null || mClosingTarget == null
-                || mCloseAnimation == null || mEnterAnimation == null) {
-            finishAnimation();
-            return;
-        }
-        mProgressSpring.cancel();
-
-        // Enter phase 2 of the animation
-        final ValueAnimator valueAnimator = ValueAnimator.ofFloat(mLatestProgress, 1f)
-                .setDuration(POST_ANIMATION_DURATION);
-        valueAnimator.setInterpolator(mDecelerateInterpolator);
-        valueAnimator.addUpdateListener(animation -> {
-            float progress = (float) animation.getAnimatedValue();
-            applyTransformTransaction(progress);
-        });
-
-        valueAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                finishAnimation();
-            }
-        });
-        valueAnimator.start();
-    }
-
-    /** Load customize animation before animation start. */
-    @Override
-    public boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo,
-            int letterboxColor) {
-        if (animationInfo == null) {
-            return false;
-        }
-        final AnimationLoadResult result = mCustomAnimationLoader.loadAll(animationInfo);
-        if (result != null) {
-            mCloseAnimation = result.mCloseAnimation;
-            mEnterAnimation = result.mEnterAnimation;
-            mNextBackgroundColor = result.mBackgroundColor;
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public BackAnimationRunner getRunner() {
-        return mBackAnimationRunner;
-    }
-
-    private final class Callback extends IOnBackInvokedCallback.Default {
-        @Override
-        public void onBackStarted(BackMotionEvent backEvent) {
-            // in case we're still animating an onBackCancelled event, let's remove the finish-
-            // callback from the progress animator to prevent calling finishAnimation() before
-            // restarting a new animation
-            mProgressAnimator.removeOnBackCancelledFinishCallback();
-
-            mProgressAnimator.onBackStarted(backEvent,
-                    CustomizeActivityAnimation.this::onGestureProgress);
-        }
-
-        @Override
-        public void onBackProgressed(@NonNull BackMotionEvent backEvent) {
-            mProgressAnimator.onBackProgressed(backEvent);
-        }
-
-        @Override
-        public void onBackCancelled() {
-            mProgressAnimator.onBackCancelled(CustomizeActivityAnimation.this::finishAnimation);
-        }
-
-        @Override
-        public void onBackInvoked() {
-            mProgressAnimator.reset();
-            onGestureCommitted();
-        }
-    }
-
-    private final class Runner extends IRemoteAnimationRunner.Default {
-        @Override
-        public void onAnimationStart(
-                int transit,
-                RemoteAnimationTarget[] apps,
-                RemoteAnimationTarget[] wallpapers,
-                RemoteAnimationTarget[] nonApps,
-                IRemoteAnimationFinishedCallback finishedCallback) {
-            ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Start back to customize animation.");
-            for (RemoteAnimationTarget a : apps) {
-                if (a.mode == MODE_CLOSING) {
-                    mClosingTarget = a;
-                }
-                if (a.mode == MODE_OPENING) {
-                    mEnteringTarget = a;
-                }
-            }
-            if (mCloseAnimation == null || mEnterAnimation == null) {
-                ProtoLog.d(WM_SHELL_BACK_PREVIEW,
-                        "No animation loaded, should choose cross-activity animation?");
-            }
-
-            startBackAnimation();
-            mFinishCallback = finishedCallback;
-        }
-
-        @Override
-        public void onAnimationCancelled() {
-            finishAnimation();
-        }
-    }
-
-
-    static final class AnimationLoadResult {
-        Animation mCloseAnimation;
-        Animation mEnterAnimation;
-        int mBackgroundColor;
-    }
-
-    /**
-     * Helper class to load custom animation.
-     */
-    static class CustomAnimationLoader {
-        final TransitionAnimation mTransitionAnimation;
-
-        CustomAnimationLoader(Context context) {
-            mTransitionAnimation = new TransitionAnimation(
-                    context, false /* debug */, "CustomizeBackAnimation");
-        }
-
-        /**
-         * Load both enter and exit animation for the close activity transition.
-         * Note that the result is only valid if the exit animation has set and loaded success.
-         * If the entering animation has not set(i.e. 0), here will load the default entering
-         * animation for it.
-         *
-         * @param animationInfo The information of customize animation, which can be set from
-         * {@link Activity#overrideActivityTransition} and/or
-         * {@link LayoutParams#windowAnimations}
-         */
-        AnimationLoadResult loadAll(BackNavigationInfo.CustomAnimationInfo animationInfo) {
-            if (animationInfo.getPackageName().isEmpty()) {
-                return null;
-            }
-            final Animation close = loadAnimation(animationInfo, false);
-            if (close == null) {
-                return null;
-            }
-            final Animation open = loadAnimation(animationInfo, true);
-            AnimationLoadResult result = new AnimationLoadResult();
-            result.mCloseAnimation = close;
-            result.mEnterAnimation = open;
-            result.mBackgroundColor = animationInfo.getCustomBackground();
-            return result;
-        }
-
-        /**
-         * Load enter or exit animation from CustomAnimationInfo
-         * @param animationInfo The information for customize animation.
-         * @param enterAnimation true when load for enter animation, false for exit animation.
-         * @return Loaded animation.
-         */
-        @Nullable
-        Animation loadAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo,
-                boolean enterAnimation) {
-            Animation a = null;
-            // Activity#overrideActivityTransition has higher priority than windowAnimations
-            // Try to get animation from Activity#overrideActivityTransition
-            if ((enterAnimation && animationInfo.getCustomEnterAnim() != 0)
-                    || (!enterAnimation && animationInfo.getCustomExitAnim() != 0)) {
-                a = mTransitionAnimation.loadAppTransitionAnimation(
-                        animationInfo.getPackageName(),
-                        enterAnimation ? animationInfo.getCustomEnterAnim()
-                                : animationInfo.getCustomExitAnim());
-            } else if (animationInfo.getWindowAnimations() != 0) {
-                // try to get animation from LayoutParams#windowAnimations
-                a = mTransitionAnimation.loadAnimationAttr(animationInfo.getPackageName(),
-                        animationInfo.getWindowAnimations(), enterAnimation
-                                ? R.styleable.WindowAnimation_activityCloseEnterAnimation
-                                : R.styleable.WindowAnimation_activityCloseExitAnimation,
-                        false /* translucent */);
-            }
-            // Only allow to load default animation for opening target.
-            if (a == null && enterAnimation) {
-                a = loadDefaultOpenAnimation();
-            }
-            if (a != null) {
-                ProtoLog.d(WM_SHELL_BACK_PREVIEW, "custom animation loaded %s", a);
-            } else {
-                ProtoLog.e(WM_SHELL_BACK_PREVIEW, "No custom animation loaded");
-            }
-            return a;
-        }
-
-        private Animation loadDefaultOpenAnimation() {
-            return mTransitionAnimation.loadDefaultAnimationAttr(
-                    R.styleable.WindowAnimation_activityCloseEnterAnimation,
-                    false /* translucent */);
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
new file mode 100644
index 0000000..f33c5b9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.back
+
+import android.content.Context
+import android.view.Choreographer
+import android.view.SurfaceControl
+import com.android.wm.shell.R
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.animation.Interpolators
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import javax.inject.Inject
+import kotlin.math.max
+
+/** Class that defines cross-activity animation. */
+@ShellMainThread
+class DefaultCrossActivityBackAnimation
+@Inject
+constructor(
+    context: Context,
+    background: BackAnimationBackground,
+    rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+) :
+    CrossActivityBackAnimation(
+        context,
+        background,
+        rootTaskDisplayAreaOrganizer,
+        SurfaceControl.Transaction(),
+        Choreographer.getInstance()
+    ) {
+
+    private val postCommitInterpolator = Interpolators.FAST_OUT_SLOW_IN
+    private val enteringStartOffset =
+        context.resources.getDimension(R.dimen.cross_activity_back_entering_start_offset)
+    override val allowEnteringYShift = true
+
+    override fun preparePreCommitEnteringRectMovement() {
+        // the entering target starts 96dp to the left of the screen edge...
+        startEnteringRect.set(startClosingRect)
+        startEnteringRect.offset(-enteringStartOffset, 0f)
+        // ...and gets scaled in sync with the closing target
+        targetEnteringRect.set(startEnteringRect)
+        targetEnteringRect.scaleCentered(MAX_SCALE)
+    }
+
+    override fun onGestureCommitted() {
+        // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current
+        // coordinate of the gesture driven phase. Let's update the start and target rects and kick
+        // off the animator in the superclass
+        startClosingRect.set(currentClosingRect)
+        startEnteringRect.set(currentEnteringRect)
+        targetEnteringRect.set(backAnimRect)
+        targetClosingRect.set(backAnimRect)
+        targetClosingRect.offset(currentClosingRect.left + enteringStartOffset, 0f)
+        super.onGestureCommitted()
+    }
+
+    override fun onPostCommitProgress(linearProgress: Float) {
+        super.onPostCommitProgress(linearProgress)
+        val closingAlpha = max(1f - linearProgress * 2, 0f)
+        val progress = postCommitInterpolator.getInterpolation(linearProgress)
+        currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress)
+        applyTransform(closingTarget?.leash, currentClosingRect, closingAlpha)
+        currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress)
+        applyTransform(enteringTarget?.leash, currentEnteringRect, 1f)
+        applyTransaction()
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 5c292f1..bfac24b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -188,6 +188,11 @@
      */
     private boolean mHasShownUserAspectRatioSettingsButton = false;
 
+    /**
+     * This is true when the rechability education is displayed for the first time.
+     */
+    private boolean mIsFirstReachabilityEducationRunning;
+
     public CompatUIController(@NonNull Context context,
             @NonNull ShellInit shellInit,
             @NonNull ShellController shellController,
@@ -252,9 +257,35 @@
             removeLayouts(taskInfo.taskId);
             return;
         }
-
+        // We're showing the first reachability education so we ignore incoming TaskInfo
+        // until the education flow has completed or we double tap.
+        if (mIsFirstReachabilityEducationRunning) {
+            return;
+        }
+        if (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed) {
+            if (taskInfo.appCompatTaskInfo.isLetterboxEducationEnabled) {
+                createOrUpdateLetterboxEduLayout(taskInfo, taskListener);
+            } else if (!taskInfo.appCompatTaskInfo.isFromLetterboxDoubleTap) {
+                // In this case the app is letterboxed and the letterbox education
+                // is disabled. In this case we need to understand if it's the first
+                // time we show the reachability education. When this is happening
+                // we need to ignore all the incoming TaskInfo until the education
+                // completes. If we come from a double tap we follow the normal flow.
+                final boolean topActivityPillarboxed =
+                        taskInfo.appCompatTaskInfo.isTopActivityPillarboxed();
+                final boolean isFirstTimeHorizontalReachabilityEdu = topActivityPillarboxed
+                        && !mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(taskInfo);
+                final boolean isFirstTimeVerticalReachabilityEdu = !topActivityPillarboxed
+                        && !mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(taskInfo);
+                if (isFirstTimeHorizontalReachabilityEdu || isFirstTimeVerticalReachabilityEdu) {
+                    mIsFirstReachabilityEducationRunning = true;
+                    mCompatUIConfiguration.setSeenLetterboxEducation(taskInfo.userId);
+                    createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
+                    return;
+                }
+            }
+        }
         createOrUpdateCompatLayout(taskInfo, taskListener);
-        createOrUpdateLetterboxEduLayout(taskInfo, taskListener);
         createOrUpdateRestartDialogLayout(taskInfo, taskListener);
         if (mCompatUIConfiguration.getHasSeenLetterboxEducation(taskInfo.userId)) {
             createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
@@ -589,6 +620,7 @@
     private void onInitialReachabilityEduDismissed(@NonNull TaskInfo taskInfo,
             @NonNull ShellTaskOrganizer.TaskListener taskListener) {
         // We need to update the UI otherwise it will not be shown until the user relaunches the app
+        mIsFirstReachabilityEducationRunning = false;
         createOrUpdateUserAspectRatioSettingsLayout(taskInfo, taskListener);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java
index 795bc1a..d2895b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java
@@ -16,9 +16,9 @@
 
 package com.android.wm.shell.dagger.back;
 
-import com.android.wm.shell.back.CrossActivityBackAnimation;
 import com.android.wm.shell.back.CrossTaskBackAnimation;
-import com.android.wm.shell.back.CustomizeActivityAnimation;
+import com.android.wm.shell.back.CustomCrossActivityBackAnimation;
+import com.android.wm.shell.back.DefaultCrossActivityBackAnimation;
 import com.android.wm.shell.back.ShellBackAnimation;
 import com.android.wm.shell.back.ShellBackAnimationRegistry;
 
@@ -47,7 +47,7 @@
     @Binds
     @ShellBackAnimation.CrossActivity
     ShellBackAnimation bindCrossActivityShellBackAnimation(
-            CrossActivityBackAnimation crossActivityBackAnimation);
+            DefaultCrossActivityBackAnimation defaultCrossActivityBackAnimation);
 
     /** Default cross task back animation */
     @Binds
@@ -59,5 +59,5 @@
     @Binds
     @ShellBackAnimation.CustomizeActivity
     ShellBackAnimation provideCustomizeActivityShellBackAnimation(
-            CustomizeActivityAnimation customizeActivityAnimation);
+            CustomCrossActivityBackAnimation customCrossActivityBackAnimation);
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
index 984abf8..bc486c2 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
@@ -32,7 +32,7 @@
 /**
  * Test launching a new activity from bubble.
  *
- * To run this test: `atest WMShellFlickerTestsBubbles:ChangeActiveActivityFromBubbleTest`
+ * To run this test: `atest WMShellFlickerTests:MultiBubblesScreen`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
index 886b70c..2a9b107 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
@@ -35,7 +35,7 @@
 /**
  * Test launching a new activity from bubble.
  *
- * To run this test: `atest WMShellFlickerTestsBubbles:DragToDismissBubbleScreenTest`
+ * To run this test: `atest WMShellFlickerTests:DismissBubbleScreen`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
index 2ee53f4..9ef49c1 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
@@ -38,7 +38,7 @@
 /**
  * Test launching a new activity from bubble.
  *
- * To run this test: `atest WMShellFlickerTestsBubbles:OpenActivityFromBubbleOnLocksreenTest`
+ * To run this test: `atest WMShellFlickerTests:OpenActivityFromBubbleOnLocksreenTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
index 463fe0e..ef7fbfb 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
@@ -29,7 +29,7 @@
 /**
  * Test launching a new activity from bubble.
  *
- * To run this test: `atest WMShellFlickerTestsBubbles:OpenActivityFromBubbleTest`
+ * To run this test: `atest WMShellFlickerTests:ExpandBubbleScreen`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
index 8df5056..87224b15 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
@@ -29,7 +29,7 @@
 /**
  * Test creating a bubble notification
  *
- * To run this test: `atest WMShellFlickerTestsBubbles:SendBubbleNotificationTest`
+ * To run this test: `atest WMShellFlickerTests:LaunchBubbleScreen`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index f99b4b2..f6f3aa4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -120,7 +120,7 @@
     private TestableContentResolver mContentResolver;
     private TestableLooper mTestableLooper;
 
-    private CrossActivityBackAnimation mCrossActivityBackAnimation;
+    private DefaultCrossActivityBackAnimation mDefaultCrossActivityBackAnimation;
     private CrossTaskBackAnimation mCrossTaskBackAnimation;
     private ShellBackAnimationRegistry mShellBackAnimationRegistry;
 
@@ -135,13 +135,14 @@
                 ANIMATION_ENABLED);
         mTestableLooper = TestableLooper.get(this);
         mShellInit = spy(new ShellInit(mShellExecutor));
-        mCrossActivityBackAnimation = new CrossActivityBackAnimation(mContext, mAnimationBackground,
-                mRootTaskDisplayAreaOrganizer);
+        mDefaultCrossActivityBackAnimation = new DefaultCrossActivityBackAnimation(mContext,
+                mAnimationBackground, mRootTaskDisplayAreaOrganizer);
         mCrossTaskBackAnimation = new CrossTaskBackAnimation(mContext, mAnimationBackground);
         mShellBackAnimationRegistry =
-                new ShellBackAnimationRegistry(mCrossActivityBackAnimation, mCrossTaskBackAnimation,
-                        /* dialogCloseAnimation= */ null,
-                        new CustomizeActivityAnimation(mContext, mAnimationBackground),
+                new ShellBackAnimationRegistry(mDefaultCrossActivityBackAnimation,
+                        mCrossTaskBackAnimation, /* dialogCloseAnimation= */ null,
+                        new CustomCrossActivityBackAnimation(mContext, mAnimationBackground,
+                                mRootTaskDisplayAreaOrganizer),
                         /* defaultBackToHomeAnimation= */ null);
         mController =
                 new BackAnimationController(
@@ -582,7 +583,7 @@
     @Test
     public void testBackToActivity() throws RemoteException {
         verifySystemBackBehavior(BackNavigationInfo.TYPE_CROSS_ACTIVITY,
-                mCrossActivityBackAnimation.getRunner());
+                mDefaultCrossActivityBackAnimation.getRunner());
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
new file mode 100644
index 0000000..8bf0111
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2023 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.f
+ */
+package com.android.wm.shell.back
+
+import android.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.AppCompatTaskInfo
+import android.app.WindowConfiguration
+import android.graphics.Color
+import android.graphics.Point
+import android.graphics.Rect
+import android.os.RemoteException
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.Choreographer
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.view.animation.Animation
+import android.window.BackEvent
+import android.window.BackMotionEvent
+import android.window.BackNavigationInfo
+import androidx.test.filters.SmallTest
+import com.android.internal.policy.TransitionAnimation
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import junit.framework.TestCase.assertEquals
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class CustomCrossActivityBackAnimationTest : ShellTestCase() {
+    @Mock private lateinit var backAnimationBackground: BackAnimationBackground
+    @Mock private lateinit var mockCloseAnimation: Animation
+    @Mock private lateinit var mockOpenAnimation: Animation
+    @Mock private lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+    @Mock private lateinit var transitionAnimation: TransitionAnimation
+    @Mock private lateinit var appCompatTaskInfo: AppCompatTaskInfo
+    @Mock private lateinit var transaction: Transaction
+
+    private lateinit var customCrossActivityBackAnimation: CustomCrossActivityBackAnimation
+    private lateinit var customAnimationLoader: CustomAnimationLoader
+
+    @Before
+    @Throws(Exception::class)
+    fun setUp() {
+        customAnimationLoader = CustomAnimationLoader(transitionAnimation)
+        customCrossActivityBackAnimation =
+            CustomCrossActivityBackAnimation(
+                context,
+                backAnimationBackground,
+                rootTaskDisplayAreaOrganizer,
+                transaction,
+                mock(Choreographer::class.java),
+                customAnimationLoader
+            )
+
+        whenever(transitionAnimation.loadAppTransitionAnimation(eq(PACKAGE_NAME), eq(OPEN_RES_ID)))
+            .thenReturn(mockOpenAnimation)
+        whenever(transitionAnimation.loadAppTransitionAnimation(eq(PACKAGE_NAME), eq(CLOSE_RES_ID)))
+            .thenReturn(mockCloseAnimation)
+        whenever(transaction.setColor(any(), any())).thenReturn(transaction)
+        whenever(transaction.setAlpha(any(), anyFloat())).thenReturn(transaction)
+        whenever(transaction.setCrop(any(), any())).thenReturn(transaction)
+        whenever(transaction.setRelativeLayer(any(), any(), anyInt())).thenReturn(transaction)
+        spy(customCrossActivityBackAnimation)
+    }
+
+    @Test
+    @Throws(InterruptedException::class)
+    fun receiveFinishAfterInvoke() {
+        val finishCalled = startCustomAnimation()
+        try {
+            customCrossActivityBackAnimation.getRunner().callback.onBackInvoked()
+        } catch (r: RemoteException) {
+            Assert.fail("onBackInvoked throw remote exception")
+        }
+        finishCalled.await(1, TimeUnit.SECONDS)
+    }
+
+    @Test
+    @Throws(InterruptedException::class)
+    fun receiveFinishAfterCancel() {
+        val finishCalled = startCustomAnimation()
+        try {
+            customCrossActivityBackAnimation.getRunner().callback.onBackCancelled()
+        } catch (r: RemoteException) {
+            Assert.fail("onBackCancelled throw remote exception")
+        }
+        finishCalled.await(1, TimeUnit.SECONDS)
+    }
+
+    @Test
+    @Throws(InterruptedException::class)
+    fun receiveFinishWithoutAnimationAfterInvoke() {
+        val finishCalled = startCustomAnimation(targets = arrayOf())
+        try {
+            customCrossActivityBackAnimation.getRunner().callback.onBackInvoked()
+        } catch (r: RemoteException) {
+            Assert.fail("onBackInvoked throw remote exception")
+        }
+        finishCalled.await(1, TimeUnit.SECONDS)
+    }
+
+    @Test
+    fun testLoadCustomAnimation() {
+        testLoadCustomAnimation(OPEN_RES_ID, CLOSE_RES_ID, 0)
+    }
+
+    @Test
+    fun testLoadCustomAnimationNoEnter() {
+        testLoadCustomAnimation(0, CLOSE_RES_ID, 0)
+    }
+
+    @Test
+    fun testLoadWindowAnimations() {
+        testLoadCustomAnimation(0, 0, 30)
+    }
+
+    @Test
+    fun testCustomAnimationHigherThanWindowAnimations() {
+        testLoadCustomAnimation(OPEN_RES_ID, CLOSE_RES_ID, 30)
+    }
+
+    private fun testLoadCustomAnimation(enterResId: Int, exitResId: Int, windowAnimations: Int) {
+        val builder =
+            BackNavigationInfo.Builder()
+                .setCustomAnimation(PACKAGE_NAME, enterResId, exitResId, Color.GREEN)
+                .setWindowAnimations(PACKAGE_NAME, windowAnimations)
+        val info = builder.build().customAnimationInfo!!
+        whenever(
+                transitionAnimation.loadAnimationAttr(
+                    eq(PACKAGE_NAME),
+                    eq(windowAnimations),
+                    anyInt(),
+                    anyBoolean()
+                )
+            )
+            .thenReturn(mockCloseAnimation)
+        whenever(transitionAnimation.loadDefaultAnimationAttr(anyInt(), anyBoolean()))
+            .thenReturn(mockOpenAnimation)
+        val result = customAnimationLoader.loadAll(info)!!
+        if (exitResId != 0) {
+            if (enterResId == 0) {
+                verify(transitionAnimation, never())
+                    .loadAppTransitionAnimation(eq(PACKAGE_NAME), eq(enterResId))
+                verify(transitionAnimation).loadDefaultAnimationAttr(anyInt(), anyBoolean())
+            } else {
+                assertEquals(result.enterAnimation, mockOpenAnimation)
+            }
+            assertEquals(result.backgroundColor.toLong(), Color.GREEN.toLong())
+            assertEquals(result.closeAnimation, mockCloseAnimation)
+            verify(transitionAnimation, never())
+                .loadAnimationAttr(eq(PACKAGE_NAME), anyInt(), anyInt(), anyBoolean())
+        } else if (windowAnimations != 0) {
+            verify(transitionAnimation, times(2))
+                .loadAnimationAttr(eq(PACKAGE_NAME), anyInt(), anyInt(), anyBoolean())
+            Assert.assertEquals(result.closeAnimation, mockCloseAnimation)
+        }
+    }
+
+    private fun startCustomAnimation(
+        targets: Array<RemoteAnimationTarget> =
+            arrayOf(createAnimationTarget(false), createAnimationTarget(true))
+    ): CountDownLatch {
+        val backNavigationInfo =
+            BackNavigationInfo.Builder()
+                .setCustomAnimation(PACKAGE_NAME, OPEN_RES_ID, CLOSE_RES_ID, /*backgroundColor*/ 0)
+                .build()
+        customCrossActivityBackAnimation.prepareNextAnimation(
+            backNavigationInfo.customAnimationInfo,
+            0
+        )
+        val finishCalled = CountDownLatch(1)
+        val finishCallback = Runnable { finishCalled.countDown() }
+        customCrossActivityBackAnimation
+            .getRunner()
+            .startAnimation(targets, null, null, finishCallback)
+        customCrossActivityBackAnimation.runner.callback.onBackStarted(backMotionEventFrom(0f, 0f))
+        if (targets.isNotEmpty()) {
+            verify(mockCloseAnimation)
+                .initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE))
+            verify(mockOpenAnimation)
+                .initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE))
+        }
+        return finishCalled
+    }
+
+    private fun backMotionEventFrom(touchX: Float, progress: Float) =
+        BackMotionEvent(
+            /* touchX = */ touchX,
+            /* touchY = */ 0f,
+            /* progress = */ progress,
+            /* velocityX = */ 0f,
+            /* velocityY = */ 0f,
+            /* triggerBack = */ false,
+            /* swipeEdge = */ BackEvent.EDGE_LEFT,
+            /* departingAnimationTarget = */ null
+        )
+
+    private fun createAnimationTarget(open: Boolean): RemoteAnimationTarget {
+        val topWindowLeash = SurfaceControl()
+        val taskInfo = RunningTaskInfo()
+        taskInfo.appCompatTaskInfo = appCompatTaskInfo
+        taskInfo.taskDescription = ActivityManager.TaskDescription()
+        return RemoteAnimationTarget(
+            1,
+            if (open) RemoteAnimationTarget.MODE_OPENING else RemoteAnimationTarget.MODE_CLOSING,
+            topWindowLeash,
+            false,
+            Rect(),
+            Rect(),
+            -1,
+            Point(0, 0),
+            Rect(0, 0, BOUND_SIZE, BOUND_SIZE),
+            Rect(),
+            WindowConfiguration(),
+            true,
+            null,
+            null,
+            taskInfo,
+            false,
+            -1
+        )
+    }
+
+    companion object {
+        private const val BOUND_SIZE = 100
+        private const val OPEN_RES_ID = 1000
+        private const val CLOSE_RES_ID = 1001
+        private const val PACKAGE_NAME = "TestPackage"
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
deleted file mode 100644
index 158d640..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.back;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.app.WindowConfiguration;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.Choreographer;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.animation.Animation;
-import android.window.BackNavigationInfo;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@SmallTest
-@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner.class)
-public class CustomizeActivityAnimationTest extends ShellTestCase {
-    private static final int BOUND_SIZE = 100;
-    @Mock
-    private BackAnimationBackground mBackAnimationBackground;
-    @Mock
-    private Animation mMockCloseAnimation;
-    @Mock
-    private Animation mMockOpenAnimation;
-
-    private CustomizeActivityAnimation mCustomizeActivityAnimation;
-
-    @Before
-    public void setUp() throws Exception {
-        mCustomizeActivityAnimation = new CustomizeActivityAnimation(mContext,
-                mBackAnimationBackground, mock(SurfaceControl.Transaction.class),
-                mock(Choreographer.class));
-        spyOn(mCustomizeActivityAnimation);
-        spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation);
-    }
-
-    RemoteAnimationTarget createAnimationTarget(boolean open) {
-        SurfaceControl topWindowLeash = new SurfaceControl();
-        return new RemoteAnimationTarget(1,
-                open ? RemoteAnimationTarget.MODE_OPENING : RemoteAnimationTarget.MODE_CLOSING,
-                topWindowLeash, false, new Rect(), new Rect(), -1,
-                new Point(0, 0), new Rect(0, 0, BOUND_SIZE, BOUND_SIZE), new Rect(),
-                new WindowConfiguration(), true, null, null, null, false, -1);
-    }
-
-    @Test
-    public void receiveFinishAfterInvoke() throws InterruptedException {
-        spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader);
-        doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
-                .loadAnimation(any(), eq(false));
-        doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
-                .loadAnimation(any(), eq(true));
-
-        mCustomizeActivityAnimation.prepareNextAnimation(
-                new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0);
-        final RemoteAnimationTarget close = createAnimationTarget(false);
-        final RemoteAnimationTarget open = createAnimationTarget(true);
-        // start animation with remote animation targets
-        final CountDownLatch finishCalled = new CountDownLatch(1);
-        final Runnable finishCallback = finishCalled::countDown;
-        mCustomizeActivityAnimation
-                .getRunner()
-                .startAnimation(
-                        new RemoteAnimationTarget[] {close, open}, null, null, finishCallback);
-        verify(mMockCloseAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
-                eq(BOUND_SIZE), eq(BOUND_SIZE));
-        verify(mMockOpenAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
-                eq(BOUND_SIZE), eq(BOUND_SIZE));
-
-        try {
-            mCustomizeActivityAnimation.getRunner().getCallback().onBackInvoked();
-        } catch (RemoteException r) {
-            fail("onBackInvoked throw remote exception");
-        }
-        verify(mCustomizeActivityAnimation).onGestureCommitted();
-        finishCalled.await(1, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void receiveFinishAfterCancel() throws InterruptedException {
-        spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader);
-        doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
-                .loadAnimation(any(), eq(false));
-        doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
-                .loadAnimation(any(), eq(true));
-
-        mCustomizeActivityAnimation.prepareNextAnimation(
-                new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0);
-        final RemoteAnimationTarget close = createAnimationTarget(false);
-        final RemoteAnimationTarget open = createAnimationTarget(true);
-        // start animation with remote animation targets
-        final CountDownLatch finishCalled = new CountDownLatch(1);
-        final Runnable finishCallback = finishCalled::countDown;
-        mCustomizeActivityAnimation
-                .getRunner()
-                .startAnimation(
-                        new RemoteAnimationTarget[] {close, open}, null, null, finishCallback);
-        verify(mMockCloseAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
-                eq(BOUND_SIZE), eq(BOUND_SIZE));
-        verify(mMockOpenAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
-                eq(BOUND_SIZE), eq(BOUND_SIZE));
-
-        try {
-            mCustomizeActivityAnimation.getRunner().getCallback().onBackCancelled();
-        } catch (RemoteException r) {
-            fail("onBackCancelled throw remote exception");
-        }
-        finishCalled.await(1, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void receiveFinishWithoutAnimationAfterInvoke() throws InterruptedException {
-        mCustomizeActivityAnimation.prepareNextAnimation(
-                new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0);
-        // start animation without any remote animation targets
-        final CountDownLatch finishCalled = new CountDownLatch(1);
-        final Runnable finishCallback = finishCalled::countDown;
-        mCustomizeActivityAnimation
-                .getRunner()
-                .startAnimation(new RemoteAnimationTarget[] {}, null, null, finishCallback);
-
-        try {
-            mCustomizeActivityAnimation.getRunner().getCallback().onBackInvoked();
-        } catch (RemoteException r) {
-            fail("onBackInvoked throw remote exception");
-        }
-        verify(mCustomizeActivityAnimation).onGestureCommitted();
-        finishCalled.await(1, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testLoadCustomAnimation() {
-        testLoadCustomAnimation(10, 20, 0);
-    }
-
-    @Test
-    public void testLoadCustomAnimationNoEnter() {
-        testLoadCustomAnimation(0, 10, 0);
-    }
-
-    @Test
-    public void testLoadWindowAnimations() {
-        testLoadCustomAnimation(0, 0, 30);
-    }
-
-    @Test
-    public void testCustomAnimationHigherThanWindowAnimations() {
-        testLoadCustomAnimation(10, 20, 30);
-    }
-
-    private void testLoadCustomAnimation(int enterResId, int exitResId, int windowAnimations) {
-        final String testPackage = "TestPackage";
-        BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder()
-                .setCustomAnimation(testPackage, enterResId, exitResId, Color.GREEN)
-                .setWindowAnimations(testPackage, windowAnimations);
-        final BackNavigationInfo.CustomAnimationInfo info = builder.build()
-                .getCustomAnimationInfo();
-
-        doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
-                        .mTransitionAnimation)
-                .loadAppTransitionAnimation(eq(testPackage), eq(enterResId));
-        doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
-                        .mTransitionAnimation)
-                .loadAppTransitionAnimation(eq(testPackage), eq(exitResId));
-        doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
-                        .mTransitionAnimation)
-                .loadAnimationAttr(eq(testPackage), eq(windowAnimations), anyInt(), anyBoolean());
-        doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
-                        .mTransitionAnimation).loadDefaultAnimationAttr(anyInt(), anyBoolean());
-
-        CustomizeActivityAnimation.AnimationLoadResult result =
-                mCustomizeActivityAnimation.mCustomAnimationLoader.loadAll(info);
-
-        if (exitResId != 0) {
-            if (enterResId == 0) {
-                verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation,
-                        never()).loadAppTransitionAnimation(eq(testPackage), eq(enterResId));
-                verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation)
-                        .loadDefaultAnimationAttr(anyInt(), anyBoolean());
-            } else {
-                assertEquals(result.mEnterAnimation, mMockOpenAnimation);
-            }
-            assertEquals(result.mBackgroundColor, Color.GREEN);
-            assertEquals(result.mCloseAnimation, mMockCloseAnimation);
-            verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, never())
-                    .loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean());
-        } else if (windowAnimations != 0) {
-            verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation,
-                    times(2)).loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean());
-            assertEquals(result.mCloseAnimation, mMockCloseAnimation);
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index afae653..9c00864 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -668,6 +668,18 @@
         Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
     }
 
+    @Test
+    public void testLetterboxEduLayout_notCreatedWhenLetterboxEducationIsDisabled() {
+        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
+                CAMERA_COMPAT_CONTROL_HIDDEN);
+        taskInfo.appCompatTaskInfo.isLetterboxEducationEnabled = false;
+
+        mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
+
+        verify(mController, never()).createLetterboxEduWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
+    }
+
     private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
             @CameraCompatControlState int cameraCompatControlState) {
         return createTaskInfo(displayId, taskId, hasSizeCompat, cameraCompatControlState,
@@ -694,6 +706,8 @@
         taskInfo.isVisible = isVisible;
         taskInfo.isFocused = isFocused;
         taskInfo.isTopActivityTransparent = isTopActivityTransparent;
+        taskInfo.appCompatTaskInfo.isLetterboxEducationEnabled = true;
+        taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed = true;
         return taskInfo;
     }
 }
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index 5cf5a1d..f1ee325 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -467,10 +467,10 @@
 
     std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1);
     /*
-     * Using ui::ADISPLAY_ID_NONE for displayId here to avoid removing the callback
+     * Using ui::LogicalDisplayId::INVALID for displayId here to avoid removing the callback
      * if a TouchSpotController with the same display is removed.
      */
-    mContext.addAnimationCallback(ui::ADISPLAY_ID_NONE, func);
+    mContext.addAnimationCallback(ui::LogicalDisplayId::INVALID, func);
 }
 
 } // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 70e5c24..c6430f7 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -109,7 +109,7 @@
 
     struct Locked {
         Presentation presentation;
-        ui::LogicalDisplayId pointerDisplayId = ui::ADISPLAY_ID_NONE;
+        ui::LogicalDisplayId pointerDisplayId = ui::LogicalDisplayId::INVALID;
 
         std::vector<gui::DisplayInfo> mDisplayInfos;
         std::unordered_map<ui::LogicalDisplayId, TouchSpotController> spotControllers;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 070c90c..e147c56 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -174,7 +174,7 @@
         int32_t layer{0};
         float alpha{1.0f};
         SpriteTransformationMatrix transformationMatrix;
-        ui::LogicalDisplayId displayId{ui::ADISPLAY_ID_DEFAULT};
+        ui::LogicalDisplayId displayId{ui::LogicalDisplayId::DEFAULT};
 
         sp<SurfaceControl> surfaceControl;
         int32_t surfaceWidth{0};
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 7a13380..2dcb1f1 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -166,7 +166,7 @@
     PointerControllerTest();
     ~PointerControllerTest();
 
-    void ensureDisplayViewportIsSet(ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_DEFAULT);
+    void ensureDisplayViewportIsSet(ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT);
 
     sp<MockSprite> mPointerSprite;
     sp<MockPointerControllerPolicyInterface> mPolicy;
@@ -335,23 +335,23 @@
 
     // Update spots to sync state with sprite
     mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
-                                 ui::ADISPLAY_ID_DEFAULT);
+                                 ui::LogicalDisplayId::DEFAULT);
     testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
 
     // Marking the display to skip screenshot should update sprite as well
-    mPointerController->setSkipScreenshot(ui::ADISPLAY_ID_DEFAULT, true);
+    mPointerController->setSkipScreenshot(ui::LogicalDisplayId::DEFAULT, true);
     EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(true));
 
     // Update spots to sync state with sprite
     mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
-                                 ui::ADISPLAY_ID_DEFAULT);
+                                 ui::LogicalDisplayId::DEFAULT);
     testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
 
     // Reset flag and verify again
-    mPointerController->setSkipScreenshot(ui::ADISPLAY_ID_DEFAULT, false);
+    mPointerController->setSkipScreenshot(ui::LogicalDisplayId::DEFAULT, false);
     EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(false));
     mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
-                                 ui::ADISPLAY_ID_DEFAULT);
+                                 ui::LogicalDisplayId::DEFAULT);
     testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
 }
 
diff --git a/packages/PrintSpooler/res/values-night/themes.xml b/packages/PrintSpooler/res/values-night/themes.xml
index 3cc64a6..76fa7b9 100644
--- a/packages/PrintSpooler/res/values-night/themes.xml
+++ b/packages/PrintSpooler/res/values-night/themes.xml
@@ -24,6 +24,7 @@
     <style name="Theme.SelectPrinterActivity"
            parent="android:style/Theme.DeviceDefault">
         <item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
     </style>
 
     <style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault">
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index bd96025..22842f7 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -24,6 +24,7 @@
            parent="android:style/Theme.DeviceDefault.Light">
         <item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
         <item name="android:windowLightStatusBar">true</item>
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
     </style>
 
     <style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault.Light">
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index 05507e0..493818b 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -80,14 +80,15 @@
                 continue;
             }
             final URLSpan urlSpan = (URLSpan) clickable;
-            if (!urlSpan.getURL().startsWith(INTENT_URL_PREFIX)) {
+            final String url = urlSpan.getURL();
+            if (url == null || !url.startsWith(INTENT_URL_PREFIX)) {
                 continue;
             }
             final int start = spannable.getSpanStart(urlSpan);
             final int end = spannable.getSpanEnd(urlSpan);
             spannable.removeSpan(urlSpan);
             try {
-                final Intent intent = Intent.parseUri(urlSpan.getURL(), Intent.URI_INTENT_SCHEME);
+                final Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
                 final ClickableSpan clickableSpan =
                         new ClickableSpan() {
                             @Override
@@ -98,7 +99,7 @@
                         };
                 spannable.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
             } catch (URISyntaxException e) {
-                Log.e(TAG, "Invalid URI " + urlSpan.getURL(), e);
+                Log.e(TAG, "Invalid URI " + url, e);
             }
         }
         title.setText(spannable);
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
index 6407810..c3a91a2 100644
--- a/packages/SettingsLib/IllustrationPreference/Android.bp
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -21,6 +21,7 @@
         "SettingsLibColor",
         "androidx.preference_preference",
         "lottie",
+        "settingslib_illustrationpreference_flags_lib",
     ],
 
     sdk_version: "system_current",
@@ -31,3 +32,24 @@
         "com.android.permission",
     ],
 }
+
+aconfig_declarations {
+    name: "settingslib_illustrationpreference_flags",
+    package: "com.android.settingslib.widget.flags",
+    container: "system",
+    srcs: [
+        "aconfig/illustrationpreference.aconfig",
+    ],
+}
+
+java_aconfig_library {
+    name: "settingslib_illustrationpreference_flags_lib",
+    aconfig_declarations: "settingslib_illustrationpreference_flags",
+
+    min_sdk_version: "30",
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.permission",
+    ],
+}
diff --git a/packages/SettingsLib/IllustrationPreference/aconfig/illustrationpreference.aconfig b/packages/SettingsLib/IllustrationPreference/aconfig/illustrationpreference.aconfig
new file mode 100644
index 0000000..e566d89
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/aconfig/illustrationpreference.aconfig
@@ -0,0 +1,12 @@
+package: "com.android.settingslib.widget.flags"
+container: "system"
+
+flag {
+    name: "auto_hide_empty_lottie_res"
+    namespace: "android_settings"
+    description: "Hides IllustrationPreference when Lottie resource is an empty file"
+    bug: "337873972"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index 815a101..bbf0315 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -39,12 +39,14 @@
 import androidx.preference.PreferenceViewHolder;
 import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
 
+import com.android.settingslib.widget.flags.Flags;
 import com.android.settingslib.widget.preference.illustration.R;
 
 import com.airbnb.lottie.LottieAnimationView;
 import com.airbnb.lottie.LottieDrawable;
 
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.InputStream;
 
 /**
@@ -142,7 +144,7 @@
         illustrationFrame.setLayoutParams(lp);
 
         illustrationView.setCacheComposition(mCacheComposition);
-        handleImageWithAnimation(illustrationView);
+        handleImageWithAnimation(illustrationView, illustrationFrame);
         handleImageFrameMaxHeight(backgroundView, illustrationView);
 
         if (mIsAutoScale) {
@@ -332,7 +334,8 @@
         }
     }
 
-    private void handleImageWithAnimation(LottieAnimationView illustrationView) {
+    private void handleImageWithAnimation(LottieAnimationView illustrationView,
+            ViewGroup container) {
         if (mImageDrawable != null) {
             resetAnimations(illustrationView);
             illustrationView.setImageDrawable(mImageDrawable);
@@ -356,6 +359,25 @@
         }
 
         if (mImageResId > 0) {
+            if (Flags.autoHideEmptyLottieRes()) {
+                // Check if resource is empty
+                try (InputStream is = illustrationView.getResources()
+                        .openRawResource(mImageResId)) {
+                    int check = is.read();
+                    // -1 = end of stream. if first read is end of stream, then file is empty
+                    if (check == -1) {
+                        illustrationView.setVisibility(View.GONE);
+                        container.setVisibility(View.GONE);
+                        return;
+                    }
+                } catch (IOException e) {
+                    Log.w(TAG, "Unable to open Lottie raw resource", e);
+                }
+
+                illustrationView.setVisibility(View.VISIBLE);
+                container.setVisibility(View.VISIBLE);
+            }
+
             resetAnimations(illustrationView);
             illustrationView.setImageResource(mImageResId);
             final Drawable drawable = illustrationView.getDrawable();
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 89f54d9..9c0d29d 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -62,3 +62,10 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+  name: "allow_all_widgets_on_lockscreen_by_default"
+  namespace: "systemui"
+  description: "Allow all widgets on the lock screen by default."
+  bug: "328261690"
+}
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index f4ddd0a..f87b519 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -51,6 +51,7 @@
         "androidx.core_core",
         "flag-junit",
         "settingslib_media_flags_lib",
+        "settingslib_illustrationpreference_flags_lib",
         "testng", // TODO: remove once JUnit on Android provides assertThrows
     ],
     java_resource_dirs: ["config"],
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
index 6590bbd..ca53fc2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -26,10 +26,14 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.drawable.AnimatedImageDrawable;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.AnimationDrawable;
 import android.net.Uri;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
@@ -39,11 +43,13 @@
 import androidx.preference.PreferenceViewHolder;
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.settingslib.widget.flags.Flags;
 import com.android.settingslib.widget.preference.illustration.R;
 
 import com.airbnb.lottie.LottieAnimationView;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -51,10 +57,14 @@
 import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
 
+import java.io.ByteArrayInputStream;
+
 
 @RunWith(RobolectricTestRunner.class)
 public class IllustrationPreferenceTest {
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Mock
     private ViewGroup mRootView;
     private Uri mImageUri;
@@ -66,6 +76,7 @@
     private final Context mContext = ApplicationProvider.getApplicationContext();
     private IllustrationPreference.OnBindListener mOnBindListener;
     private LottieAnimationView mOnBindListenerAnimationView;
+    private FrameLayout mIllustrationFrame;
 
     @Before
     public void setUp() {
@@ -75,14 +86,14 @@
         mBackgroundView = new ImageView(mContext);
         mAnimationView = spy(new LottieAnimationView(mContext));
         mMiddleGroundLayout = new FrameLayout(mContext);
-        final FrameLayout illustrationFrame = new FrameLayout(mContext);
-        illustrationFrame.setLayoutParams(
+        mIllustrationFrame = new FrameLayout(mContext);
+        mIllustrationFrame.setLayoutParams(
                 new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                         ViewGroup.LayoutParams.WRAP_CONTENT));
         doReturn(mMiddleGroundLayout).when(mRootView).findViewById(R.id.middleground_layout);
         doReturn(mBackgroundView).when(mRootView).findViewById(R.id.background_view);
         doReturn(mAnimationView).when(mRootView).findViewById(R.id.lottie_view);
-        doReturn(illustrationFrame).when(mRootView).findViewById(R.id.illustration_frame);
+        doReturn(mIllustrationFrame).when(mRootView).findViewById(R.id.illustration_frame);
         mViewHolder = spy(PreferenceViewHolder.createInstanceForTests(mRootView));
 
         final AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
@@ -158,11 +169,13 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
     public void playLottieAnimationWithResource_verifyFailureListener() {
         // fake the valid lottie image
         final int fakeValidResId = 111;
         doNothing().when(mAnimationView).setImageResource(fakeValidResId);
         doReturn(null).when(mAnimationView).getDrawable();
+        doNothing().when(mAnimationView).setAnimation(fakeValidResId);
 
         mPreference.setLottieAnimationResId(fakeValidResId);
         mPreference.onBindViewHolder(mViewHolder);
@@ -171,6 +184,50 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
+    public void handleImageWithAnimation_emptyInputStreamDisabledFlag_verifyContainerVisible() {
+        doNothing().when(mAnimationView).setImageResource(111);
+        doReturn(null).when(mAnimationView).getDrawable();
+
+        mPreference.setLottieAnimationResId(111);
+        mPreference.onBindViewHolder(mViewHolder);
+
+        assertThat(mAnimationView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mIllustrationFrame.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
+    public void handleImageWithAnimation_emptyInputStreamEnabledFlag_verifyContainerHidden() {
+        Resources res = spy(mContext.getResources());
+        doReturn(res).when(mAnimationView).getResources();
+        doReturn(new ByteArrayInputStream(new byte[] {})).when(res).openRawResource(111);
+
+        mPreference.setLottieAnimationResId(111);
+        mPreference.onBindViewHolder(mViewHolder);
+
+        assertThat(mAnimationView.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mIllustrationFrame.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
+    public void handleImageWithAnimation_nonEmptyInputStreamEnabledFlag_verifyContainerVisible() {
+        Resources res = spy(mContext.getResources());
+        doReturn(res).when(mAnimationView).getResources();
+        doReturn(new ByteArrayInputStream(new byte[] { 1, 2, 3 })).when(res).openRawResource(111);
+        doNothing().when(mAnimationView).setImageResource(111);
+        doNothing().when(mAnimationView).setAnimation(111);
+        doReturn(null).when(mAnimationView).getDrawable();
+
+        mPreference.setLottieAnimationResId(111);
+        mPreference.onBindViewHolder(mViewHolder);
+
+        assertThat(mAnimationView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mIllustrationFrame.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
     public void setMaxHeight_smallerThanRestrictedHeight_matchResult() {
         final int restrictedHeight =
                 mContext.getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 626e219..582b6a1 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -470,6 +470,17 @@
 }
 
 flag {
+    name: "fix_screenshot_action_dismiss_system_windows"
+    namespace: "systemui"
+    description: "Dismiss existing system windows when starting action from screenshot UI"
+    bug: "309933761"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+
+flag {
     name: "screenshot_private_profile_behavior_fix"
     namespace: "systemui"
     description: "Private profile support for screenshots"
@@ -891,4 +902,14 @@
   metadata {
     purpose: PURPOSE_BUGFIX
   }
-}
\ No newline at end of file
+}
+
+flag {
+  name: "media_controls_user_initiated_dismiss"
+  namespace: "systemui"
+  description: "Only dismiss media notifications when the control was removed by the user."
+  bug: "335875159"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/CollectAsStateDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/CollectAsStateDetector.kt
new file mode 100644
index 0000000..94620c4
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/CollectAsStateDetector.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 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.internal.systemui.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UFile
+import org.jetbrains.uast.UImportStatement
+
+class CollectAsStateDetector : Detector(), SourceCodeScanner {
+
+    override fun getApplicableUastTypes(): List<Class<out UElement>> {
+        return listOf(UFile::class.java)
+    }
+
+    override fun createUastHandler(context: JavaContext): UElementHandler {
+        return object : UElementHandler() {
+            override fun visitFile(node: UFile) {
+                node.imports.forEach { importStatement ->
+                    visitImportStatement(context, importStatement)
+                }
+            }
+        }
+    }
+
+    private fun visitImportStatement(
+        context: JavaContext,
+        importStatement: UImportStatement,
+    ) {
+        val importText = importStatement.importReference?.asSourceString() ?: return
+        if (ILLEGAL_IMPORT == importText) {
+            context.report(
+                issue = ISSUE,
+                scope = importStatement,
+                location = context.getLocation(importStatement),
+                message = "collectAsState considered harmful",
+            )
+        }
+    }
+
+    companion object {
+        @JvmField
+        val ISSUE =
+            Issue.create(
+                id = "OverlyEagerCollectAsState",
+                briefDescription = "collectAsState considered harmful",
+                explanation =
+                    """
+                go/sysui-compose#collect-as-state
+
+                Don't use collectAsState as it will set up a coroutine that keeps collecting from a
+                flow until its coroutine scope becomes inactive. This prevents the work from being
+                properly paused while the surrounding lifecycle becomes paused or stopped and is
+                therefore considered harmful.
+
+                Instead, use Flow.collectAsStateWithLifecycle(initial: T) or
+                StateFlow.collectAsStateWithLifecycle(). These APIs correctly pause the collection
+                coroutine while the lifecycle drops below the specified minActiveState (which
+                defaults to STARTED meaning that it will pause when the Compose-hosting window
+                becomes invisible).
+            """
+                        .trimIndent(),
+                category = Category.PERFORMANCE,
+                priority = 8,
+                severity = Severity.ERROR,
+                implementation =
+                    Implementation(
+                        CollectAsStateDetector::class.java,
+                        Scope.JAVA_FILE_SCOPE,
+                    ),
+            )
+
+        private val ILLEGAL_IMPORT = "androidx.compose.runtime.collectAsState"
+    }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index cecbc47..73ac6cc 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -32,6 +32,7 @@
                 BindServiceOnMainThreadDetector.ISSUE,
                 BroadcastSentViaContextDetector.ISSUE,
                 CleanArchitectureDependencyViolationDetector.ISSUE,
+                CollectAsStateDetector.ISSUE,
                 DumpableNotRegisteredDetector.ISSUE,
                 FlowDetector.SHARED_FLOW_CREATION,
                 SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CollectAsStateDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CollectAsStateDetectorTest.kt
new file mode 100644
index 0000000..6962b4e
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CollectAsStateDetectorTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 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.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class CollectAsStateDetectorTest : SystemUILintDetectorTest() {
+
+    override fun getDetector(): Detector {
+        return CollectAsStateDetector()
+    }
+
+    override fun getIssues(): List<Issue> {
+        return listOf(
+            CollectAsStateDetector.ISSUE,
+        )
+    }
+
+    @Test
+    fun testViolation() {
+        lint()
+            .files(COLLECT_AS_STATE_STUB, COLLECT_WITH_LIFECYCLE_AS_STATE_STUB, GOOD_FILE, BAD_FILE)
+            .issues(CollectAsStateDetector.ISSUE)
+            .run()
+            .expect(
+                """
+src/com/android/internal/systemui/lint/Bad.kt:3: Error: collectAsState considered harmful [OverlyEagerCollectAsState]
+import androidx.compose.runtime.collectAsState
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+                """
+                    .trimIndent()
+            )
+    }
+
+    @Test
+    fun testNoViolation() {
+        lint()
+            .files(COLLECT_AS_STATE_STUB, COLLECT_WITH_LIFECYCLE_AS_STATE_STUB, GOOD_FILE)
+            .issues(CollectAsStateDetector.ISSUE)
+            .run()
+            .expectClean()
+    }
+
+    companion object {
+        private val COLLECT_AS_STATE_STUB =
+            TestFiles.kotlin(
+                """
+                package androidx.compose.runtime
+
+                fun collectAsState() {}
+            """
+                    .trimIndent()
+            )
+        private val COLLECT_WITH_LIFECYCLE_AS_STATE_STUB =
+            TestFiles.kotlin(
+                """
+                package androidx.lifecycle.compose
+
+                fun collectAsStateWithLifecycle() {}
+            """
+                    .trimIndent()
+            )
+
+        private val BAD_FILE =
+            TestFiles.kotlin(
+                """
+                package com.android.internal.systemui.lint
+
+                import androidx.compose.runtime.collectAsState
+
+                class Bad
+            """
+                    .trimIndent()
+            )
+
+        private val GOOD_FILE =
+            TestFiles.kotlin(
+                """
+                package com.android.internal.systemui.lint
+
+                import androidx.lifecycle.compose.collectAsStateWithLifecycle
+
+                class Good
+            """
+                    .trimIndent()
+            )
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index c22b50d..fa01a4b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -56,7 +56,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -77,6 +76,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.unit.times
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformButton
 import com.android.compose.animation.Easings
 import com.android.compose.animation.scene.ElementKey
@@ -111,7 +111,7 @@
     dialogFactory: BouncerDialogFactory,
     modifier: Modifier = Modifier,
 ) {
-    val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsState()
+    val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsStateWithLifecycle()
     val layout = calculateLayout(isSideBySideSupported = isSideBySideSupported)
 
     Box(
@@ -220,7 +220,7 @@
     viewModel: BouncerViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val authMethod by viewModel.authMethodViewModel.collectAsState()
+    val authMethod by viewModel.authMethodViewModel.collectAsStateWithLifecycle()
 
     Row(
         modifier =
@@ -316,7 +316,7 @@
     val (isSwapped, setSwapped) = rememberSaveable(isLeftToRight) { mutableStateOf(!isLeftToRight) }
     val isHeightExpanded =
         LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded
-    val authMethod by viewModel.authMethodViewModel.collectAsState()
+    val authMethod by viewModel.authMethodViewModel.collectAsStateWithLifecycle()
 
     Row(
         modifier =
@@ -480,7 +480,7 @@
     modifier: Modifier = Modifier,
 ) {
     val foldPosture: FoldPosture by foldPosture()
-    val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsState()
+    val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsStateWithLifecycle()
     val isSplitAroundTheFold = foldPosture == FoldPosture.Tabletop && isSplitAroundTheFoldRequired
     val currentSceneKey =
         if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey
@@ -562,7 +562,7 @@
     viewModel: BouncerMessageViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val message: MessageViewModel? by viewModel.message.collectAsState()
+    val message: MessageViewModel? by viewModel.message.collectAsStateWithLifecycle()
 
     DisposableEffect(Unit) {
         viewModel.onShown()
@@ -612,7 +612,7 @@
     modifier: Modifier = Modifier,
 ) {
     val authMethodViewModel: AuthMethodBouncerViewModel? by
-        viewModel.authMethodViewModel.collectAsState()
+        viewModel.authMethodViewModel.collectAsStateWithLifecycle()
 
     when (val nonNullViewModel = authMethodViewModel) {
         is PinBouncerViewModel ->
@@ -642,7 +642,7 @@
     modifier: Modifier = Modifier,
 ) {
     val authMethodViewModel: AuthMethodBouncerViewModel? by
-        viewModel.authMethodViewModel.collectAsState()
+        viewModel.authMethodViewModel.collectAsStateWithLifecycle()
 
     when (val nonNullViewModel = authMethodViewModel) {
         is PinBouncerViewModel -> {
@@ -668,7 +668,8 @@
     viewModel: BouncerViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState()
+    val actionButton: BouncerActionButtonModel? by
+        viewModel.actionButton.collectAsStateWithLifecycle()
     val appearFadeInAnimatable = remember { Animatable(0f) }
     val appearMoveAnimatable = remember { Animatable(0f) }
     val appearAnimationInitialOffset = with(LocalDensity.current) { 80.dp.toPx() }
@@ -735,7 +736,7 @@
     bouncerViewModel: BouncerViewModel,
     dialogFactory: BouncerDialogFactory,
 ) {
-    val dialogViewModel by bouncerViewModel.dialogViewModel.collectAsState()
+    val dialogViewModel by bouncerViewModel.dialogViewModel.collectAsStateWithLifecycle()
     var dialog: AlertDialog? by remember { mutableStateOf(null) }
 
     dialogViewModel?.let { viewModel ->
@@ -772,8 +773,8 @@
         return
     }
 
-    val selectedUserImage by viewModel.selectedUserImage.collectAsState(null)
-    val dropdownItems by viewModel.userSwitcherDropdown.collectAsState(emptyList())
+    val selectedUserImage by viewModel.selectedUserImage.collectAsStateWithLifecycle(null)
+    val dropdownItems by viewModel.userSwitcherDropdown.collectAsStateWithLifecycle(emptyList())
 
     Column(
         horizontalAlignment = Alignment.CenterHorizontally,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index 2dcd0ff..203bd7a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -27,7 +27,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.ExperimentalComposeUiApi
@@ -49,6 +48,7 @@
 import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformIconButton
 import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
 import com.android.systemui.common.ui.compose.SelectedUserAwareInputConnection
@@ -62,18 +62,20 @@
     modifier: Modifier = Modifier,
 ) {
     val focusRequester = remember { FocusRequester() }
-    val isTextFieldFocusRequested by viewModel.isTextFieldFocusRequested.collectAsState()
+    val isTextFieldFocusRequested by
+        viewModel.isTextFieldFocusRequested.collectAsStateWithLifecycle()
     LaunchedEffect(isTextFieldFocusRequested) {
         if (isTextFieldFocusRequested) {
             focusRequester.requestFocus()
         }
     }
 
-    val password: String by viewModel.password.collectAsState()
-    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
-    val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
-    val isImeSwitcherButtonVisible by viewModel.isImeSwitcherButtonVisible.collectAsState()
-    val selectedUserId by viewModel.selectedUserId.collectAsState()
+    val password: String by viewModel.password.collectAsStateWithLifecycle()
+    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsStateWithLifecycle()
+    val animateFailure: Boolean by viewModel.animateFailure.collectAsStateWithLifecycle()
+    val isImeSwitcherButtonVisible by
+        viewModel.isImeSwitcherButtonVisible.collectAsStateWithLifecycle()
+    val selectedUserId by viewModel.selectedUserId.collectAsStateWithLifecycle()
 
     DisposableEffect(Unit) { onDispose { viewModel.onHidden() } }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index d7e9c10..9c2fd64 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -30,7 +30,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -47,6 +46,7 @@
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.res.integerResource
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.Easings
 import com.android.compose.modifiers.thenIf
 import com.android.internal.R
@@ -86,14 +86,15 @@
     val lineStrokeWidth = with(density) { LINE_STROKE_WIDTH_DP.dp.toPx() }
 
     // All dots that should be rendered on the grid.
-    val dots: List<PatternDotViewModel> by viewModel.dots.collectAsState()
+    val dots: List<PatternDotViewModel> by viewModel.dots.collectAsStateWithLifecycle()
     // The most recently selected dot, if the user is currently dragging.
-    val currentDot: PatternDotViewModel? by viewModel.currentDot.collectAsState()
+    val currentDot: PatternDotViewModel? by viewModel.currentDot.collectAsStateWithLifecycle()
     // The dots selected so far, if the user is currently dragging.
-    val selectedDots: List<PatternDotViewModel> by viewModel.selectedDots.collectAsState()
-    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
-    val isAnimationEnabled: Boolean by viewModel.isPatternVisible.collectAsState()
-    val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
+    val selectedDots: List<PatternDotViewModel> by
+        viewModel.selectedDots.collectAsStateWithLifecycle()
+    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsStateWithLifecycle()
+    val isAnimationEnabled: Boolean by viewModel.isPatternVisible.collectAsStateWithLifecycle()
+    val animateFailure: Boolean by viewModel.animateFailure.collectAsStateWithLifecycle()
 
     // Map of animatables for the scale of each dot, keyed by dot.
     val dotScalingAnimatables = remember(dots) { dots.associateWith { Animatable(1f) } }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 5651a46..64ace2f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -33,7 +33,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -49,6 +48,7 @@
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.Easings
 import com.android.compose.grid.VerticalGrid
 import com.android.compose.modifiers.thenIf
@@ -74,12 +74,13 @@
 ) {
     DisposableEffect(Unit) { onDispose { viewModel.onHidden() } }
 
-    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
-    val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState()
-    val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsState()
-    val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
+    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsStateWithLifecycle()
+    val backspaceButtonAppearance by
+        viewModel.backspaceButtonAppearance.collectAsStateWithLifecycle()
+    val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsStateWithLifecycle()
+    val animateFailure: Boolean by viewModel.animateFailure.collectAsStateWithLifecycle()
     val isDigitButtonAnimationEnabled: Boolean by
-        viewModel.isDigitButtonAnimationEnabled.collectAsState()
+        viewModel.isDigitButtonAnimationEnabled.collectAsStateWithLifecycle()
 
     val buttonScaleAnimatables = remember { List(12) { Animatable(1f) } }
     LaunchedEffect(animateFailure) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
index 1a97912..465eade 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
@@ -42,7 +42,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
 import androidx.compose.runtime.mutableStateListOf
@@ -65,6 +64,7 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.Dialog
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformOutlinedButton
 import com.android.compose.animation.Easings
 import com.android.keyguard.PinShapeAdapter
@@ -86,7 +86,7 @@
     viewModel: PinBouncerViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val hintedPinLength: Int? by viewModel.hintedPinLength.collectAsState()
+    val hintedPinLength: Int? by viewModel.hintedPinLength.collectAsStateWithLifecycle()
     val shapeAnimations = rememberShapeAnimations(viewModel.pinShapes)
 
     // The display comes in two different flavors:
@@ -119,7 +119,7 @@
     hintedPinLength: Int,
     modifier: Modifier = Modifier,
 ) {
-    val pinInput: PinInputViewModel by viewModel.pinInput.collectAsState()
+    val pinInput: PinInputViewModel by viewModel.pinInput.collectAsStateWithLifecycle()
     // [ClearAll] marker pointing at the beginning of the current pin input.
     // When a new [ClearAll] token is added to the [pinInput], the clear-all animation is played
     // and the marker is advanced manually to the most recent marker. See LaunchedEffect below.
@@ -257,9 +257,10 @@
 
 @Composable
 private fun SimArea(viewModel: PinBouncerViewModel) {
-    val isLockedEsim by viewModel.isLockedEsim.collectAsState()
-    val isSimUnlockingDialogVisible by viewModel.isSimUnlockingDialogVisible.collectAsState()
-    val errorDialogMessage by viewModel.errorDialogMessage.collectAsState()
+    val isLockedEsim by viewModel.isLockedEsim.collectAsStateWithLifecycle()
+    val isSimUnlockingDialogVisible by
+        viewModel.isSimUnlockingDialogVisible.collectAsStateWithLifecycle()
+    val errorDialogMessage by viewModel.errorDialogMessage.collectAsStateWithLifecycle()
     var unlockDialog: Dialog? by remember { mutableStateOf(null) }
     var errorDialog: Dialog? by remember { mutableStateOf(null) }
     val context = LocalView.current.context
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt
index c8e1450..694326d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt
@@ -35,7 +35,7 @@
  * ```
  * @Composable
  * fun YourFunction(viewModel: YourViewModel) {
- *     val selectedUserId by viewModel.selectedUserId.collectAsState()
+ *     val selectedUserId by viewModel.selectedUserId.collectAsStateWithLifecycle()
  *
  *     SelectedUserAwareInputConnection(selectedUserId) {
  *         TextField(...)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt
index 8144d15..296fc27 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt
@@ -22,12 +22,12 @@
 import androidx.compose.foundation.layout.systemBars
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.staticCompositionLocalOf
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import kotlinx.coroutines.flow.StateFlow
 
 /** The bounds and [CutoutLocation] of the current display. */
@@ -45,7 +45,7 @@
     screenCornerRadius: Float,
     content: @Composable () -> Unit,
 ) {
-    val cutout by displayCutout.collectAsState()
+    val cutout by displayCutout.collectAsStateWithLifecycle()
     val screenCornerRadiusDp = with(LocalDensity.current) { screenCornerRadius.toDp() }
 
     val density = LocalDensity.current
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 08e452c..8ee8ea4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -6,13 +6,13 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.res.dimensionResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
@@ -85,8 +85,9 @@
     content: CommunalContent,
 ) {
     val coroutineScope = rememberCoroutineScope()
-    val currentSceneKey: SceneKey by viewModel.currentScene.collectAsState(CommunalScenes.Blank)
-    val touchesAllowed by viewModel.touchesAllowed.collectAsState(initial = false)
+    val currentSceneKey: SceneKey by
+        viewModel.currentScene.collectAsStateWithLifecycle(CommunalScenes.Blank)
+    val touchesAllowed by viewModel.touchesAllowed.collectAsStateWithLifecycle(initialValue = false)
     val state: MutableSceneTransitionLayoutState = remember {
         MutableSceneTransitionLayoutState(
             initialScene = currentSceneKey,
@@ -149,7 +150,8 @@
     content: CommunalContent,
     modifier: Modifier = Modifier,
 ) {
-    val backgroundColor by colors.backgroundColor.collectAsState()
+    val backgroundColor by colors.backgroundColor.collectAsStateWithLifecycle()
+
     Box(
         modifier =
             Modifier.element(Communal.Elements.Scrim)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 02621f6..2a52c60 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -78,7 +78,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.State
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -124,6 +123,7 @@
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.compose.ui.window.Popup
 import androidx.core.view.setPadding
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.window.layout.WindowMetricsCalculator
 import com.android.compose.modifiers.thenIf
 import com.android.compose.theme.LocalAndroidColorScheme
@@ -156,20 +156,21 @@
     onOpenWidgetPicker: (() -> Unit)? = null,
     onEditDone: (() -> Unit)? = null,
 ) {
-    val communalContent by viewModel.communalContent.collectAsState(initial = emptyList())
-    val currentPopup by viewModel.currentPopup.collectAsState(initial = null)
+    val communalContent by
+        viewModel.communalContent.collectAsStateWithLifecycle(initialValue = emptyList())
+    val currentPopup by viewModel.currentPopup.collectAsStateWithLifecycle(initialValue = null)
     var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
     var toolbarSize: IntSize? by remember { mutableStateOf(null) }
     var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
     var isDraggingToRemove by remember { mutableStateOf(false) }
     val gridState = rememberLazyGridState()
     val contentListState = rememberContentListState(widgetConfigurator, communalContent, viewModel)
-    val reorderingWidgets by viewModel.reorderingWidgets.collectAsState()
-    val selectedKey = viewModel.selectedKey.collectAsState()
+    val reorderingWidgets by viewModel.reorderingWidgets.collectAsStateWithLifecycle()
+    val selectedKey = viewModel.selectedKey.collectAsStateWithLifecycle()
     val removeButtonEnabled by remember {
         derivedStateOf { selectedKey.value != null || reorderingWidgets }
     }
-    val isEmptyState by viewModel.isEmptyState.collectAsState(initial = false)
+    val isEmptyState by viewModel.isEmptyState.collectAsStateWithLifecycle(initialValue = false)
 
     val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize)
     val contentOffset = beforeContentPadding(contentPadding).toOffset()
@@ -303,9 +304,9 @@
 
         if (viewModel is CommunalViewModel && dialogFactory != null) {
             val isEnableWidgetDialogShowing by
-                viewModel.isEnableWidgetDialogShowing.collectAsState(false)
+                viewModel.isEnableWidgetDialogShowing.collectAsStateWithLifecycle(false)
             val isEnableWorkProfileDialogShowing by
-                viewModel.isEnableWorkProfileDialogShowing.collectAsState(false)
+                viewModel.isEnableWorkProfileDialogShowing.collectAsStateWithLifecycle(false)
 
             EnableWidgetDialog(
                 isEnableWidgetDialogVisible = isEnableWidgetDialogShowing,
@@ -860,7 +861,7 @@
     contentListState: ContentListState,
 ) {
     val context = LocalContext.current
-    val isFocusable by viewModel.isFocusable.collectAsState(initial = false)
+    val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
     val accessibilityLabel =
         remember(model, context) {
             model.providerInfo.loadLabel(context.packageManager).toString().trim()
@@ -868,7 +869,7 @@
     val clickActionLabel = stringResource(R.string.accessibility_action_label_select_widget)
     val removeWidgetActionLabel = stringResource(R.string.accessibility_action_label_remove_widget)
     val placeWidgetActionLabel = stringResource(R.string.accessibility_action_label_place_widget)
-    val selectedKey by viewModel.selectedKey.collectAsState()
+    val selectedKey by viewModel.selectedKey.collectAsStateWithLifecycle()
     val selectedIndex =
         selectedKey?.let { key -> contentListState.list.indexOfFirst { it.key == key } }
     Box(
@@ -1109,7 +1110,7 @@
 @Composable
 fun AccessibilityContainer(viewModel: BaseCommunalViewModel, content: @Composable () -> Unit) {
     val context = LocalContext.current
-    val isFocusable by viewModel.isFocusable.collectAsState(initial = false)
+    val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
     Box(
         modifier =
             Modifier.fillMaxWidth().wrapContentHeight().thenIf(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt b/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
index e77ade9..17dac7e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
@@ -18,11 +18,11 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.produceState
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.window.layout.WindowInfoTracker
 import com.android.systemui.fold.ui.helper.FoldPosture
 import com.android.systemui.fold.ui.helper.foldPostureInternal
@@ -32,7 +32,8 @@
 fun foldPosture(): State<FoldPosture> {
     val context = LocalContext.current
     val infoTracker = remember(context) { WindowInfoTracker.getOrCreate(context) }
-    val layoutInfo by infoTracker.windowLayoutInfo(context).collectAsState(initial = null)
+    val layoutInfo by
+        infoTracker.windowLayoutInfo(context).collectAsStateWithLifecycle(initialValue = null)
 
     return produceState<FoldPosture>(
         initialValue = FoldPosture.Folded,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
index a8d801a..67840c7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
@@ -29,7 +29,6 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
 import androidx.compose.ui.Alignment
@@ -37,6 +36,7 @@
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.theme.PlatformTheme
 import com.android.systemui.keyboard.stickykeys.shared.model.Locked
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey
@@ -57,7 +57,7 @@
 
 @Composable
 fun StickyKeysIndicator(viewModel: StickyKeysIndicatorViewModel) {
-    val stickyKeys by viewModel.indicatorContent.collectAsState(emptyMap())
+    val stickyKeys by viewModel.indicatorContent.collectAsStateWithLifecycle(emptyMap())
     StickyKeysIndicator(stickyKeys)
 }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
index 4bef9ef..6d8c47d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
@@ -18,11 +18,11 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
@@ -51,7 +51,7 @@
         modifier: Modifier = Modifier,
     ) {
         val coroutineScope = rememberCoroutineScope()
-        val blueprintId by viewModel.blueprintId(coroutineScope).collectAsState()
+        val blueprintId by viewModel.blueprintId(coroutineScope).collectAsStateWithLifecycle()
         val view = LocalView.current
         DisposableEffect(view) {
             clockInteractor.clockEventController.registerListeners(view)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
index 472484a..4555f13 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
@@ -26,13 +26,13 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
 
 /** Container for lockscreen content that handles long-press to bring up the settings menu. */
@@ -42,7 +42,8 @@
     modifier: Modifier = Modifier,
     content: @Composable BoxScope.(onSettingsMenuPlaces: (coordinates: Rect?) -> Unit) -> Unit,
 ) {
-    val isEnabled: Boolean by viewModel.isLongPressHandlingEnabled.collectAsState(initial = false)
+    val isEnabled: Boolean by
+        viewModel.isLongPressHandlingEnabled.collectAsStateWithLifecycle(initialValue = false)
     val (settingsMenuBounds, setSettingsMenuBounds) = remember { mutableStateOf<Rect?>(null) }
     val interactionSource = remember { MutableInteractionSource() }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
index 8129e41..ba25719 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
@@ -21,11 +21,11 @@
 import androidx.compose.foundation.layout.systemBars
 import androidx.compose.foundation.layout.union
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalDensity
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
 import com.android.systemui.plugins.clocks.ClockController
@@ -37,7 +37,7 @@
 fun rememberBurnIn(
     clockInteractor: KeyguardClockInteractor,
 ): BurnInState {
-    val clock by clockInteractor.currentClock.collectAsState()
+    val clock by clockInteractor.currentClock.collectAsStateWithLifecycle()
 
     val (smartspaceTop, onSmartspaceTopChanged) = remember { mutableStateOf<Float?>(null) }
     val (smallClockTop, onSmallClockTopChanged) = remember { mutableStateOf<Float?>(null) }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 3152535..abff93d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -22,13 +22,13 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.unit.IntRect
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
 import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
@@ -67,8 +67,8 @@
     override fun SceneScope.Content(modifier: Modifier) {
         val isUdfpsVisible = viewModel.isUdfpsVisible
         val shouldUseSplitNotificationShade by
-            viewModel.shouldUseSplitNotificationShade.collectAsState()
-        val unfoldTranslations by viewModel.unfoldTranslations.collectAsState()
+            viewModel.shouldUseSplitNotificationShade.collectAsStateWithLifecycle()
+        val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle()
 
         LockscreenLongPress(
             viewModel = viewModel.longPress,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index 9d31955..c83f62c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -22,13 +22,13 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.unit.IntRect
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
 import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
@@ -70,8 +70,8 @@
     override fun SceneScope.Content(modifier: Modifier) {
         val isUdfpsVisible = viewModel.isUdfpsVisible
         val shouldUseSplitNotificationShade by
-            viewModel.shouldUseSplitNotificationShade.collectAsState()
-        val unfoldTranslations by viewModel.unfoldTranslations.collectAsState()
+            viewModel.shouldUseSplitNotificationShade.collectAsStateWithLifecycle()
+        val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle()
 
         LockscreenLongPress(
             viewModel = viewModel.longPress,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
index abbf0ea..aaf49ff 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.keyguard.ui.composable.modifier
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -25,6 +24,7 @@
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.boundsInWindow
 import androidx.compose.ui.layout.onPlaced
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
 import com.android.systemui.keyguard.ui.viewmodel.BurnInScaleViewModel
@@ -44,8 +44,10 @@
     val translationYState = remember { mutableStateOf(0F) }
     val copiedParams = params.copy(translationY = { translationYState.value })
     val burnIn = viewModel.movement(copiedParams)
-    val translationX by burnIn.map { it.translationX.toFloat() }.collectAsState(initial = 0f)
-    val translationY by burnIn.map { it.translationY.toFloat() }.collectAsState(initial = 0f)
+    val translationX by
+        burnIn.map { it.translationX.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
+    val translationY by
+        burnIn.map { it.translationY.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
     translationYState.value = translationY
     val scaleViewModel by
         burnIn
@@ -55,7 +57,7 @@
                     scaleClockOnly = it.scaleClockOnly,
                 )
             }
-            .collectAsState(initial = BurnInScaleViewModel())
+            .collectAsStateWithLifecycle(initialValue = BurnInScaleViewModel())
 
     return this.graphicsLayer {
         this.translationX = if (isClock) 0F else translationX
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index 09ec76d..218779d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -25,13 +25,13 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.core.view.contains
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
 import com.android.systemui.customization.R
@@ -59,9 +59,11 @@
         onTopChanged: (top: Float?) -> Unit,
         modifier: Modifier = Modifier,
     ) {
-        val currentClock by viewModel.currentClock.collectAsState()
+        val currentClock by viewModel.currentClock.collectAsStateWithLifecycle()
         val smallTopMargin by
-            viewModel.smallClockTopMargin.collectAsState(viewModel.getSmallClockTopMargin())
+            viewModel.smallClockTopMargin.collectAsStateWithLifecycle(
+                viewModel.getSmallClockTopMargin()
+            )
         if (currentClock?.smallClock?.view == null) {
             return
         }
@@ -89,7 +91,7 @@
 
     @Composable
     fun SceneScope.LargeClock(burnInParams: BurnInParameters, modifier: Modifier = Modifier) {
-        val currentClock by viewModel.currentClock.collectAsState()
+        val currentClock by viewModel.currentClock.collectAsStateWithLifecycle()
         if (currentClock?.largeClock?.view == null) {
             return
         }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
index c37d626..3ca2b9c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
@@ -18,9 +18,9 @@
 
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardMediaViewModel
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
@@ -40,7 +40,7 @@
 
     @Composable
     fun SceneScope.KeyguardMediaCarousel() {
-        val isMediaVisible by keyguardMediaViewModel.isMediaVisible.collectAsState()
+        val isMediaVisible by keyguardMediaViewModel.isMediaVisible.collectAsStateWithLifecycle()
 
         MediaCarousel(
             isVisible = isMediaVisible,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index f48fa88..7f80dfa 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -20,13 +20,13 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.thenIf
 import com.android.systemui.Flags
@@ -40,16 +40,19 @@
 import com.android.systemui.res.R
 import com.android.systemui.shade.LargeScreenHeaderHelper
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
 import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import dagger.Lazy
 import javax.inject.Inject
 
 @SysUISingleton
 class NotificationSection
 @Inject
 constructor(
+    private val stackScrollView: Lazy<NotificationScrollView>,
     private val viewModel: NotificationsPlaceholderViewModel,
     private val aodBurnInViewModel: AodBurnInViewModel,
     sharedNotificationContainer: SharedNotificationContainer,
@@ -88,9 +91,9 @@
     @Composable
     fun SceneScope.Notifications(burnInParams: BurnInParameters?, modifier: Modifier = Modifier) {
         val shouldUseSplitNotificationShade by
-            lockscreenContentViewModel.shouldUseSplitNotificationShade.collectAsState()
+            lockscreenContentViewModel.shouldUseSplitNotificationShade.collectAsStateWithLifecycle()
         val areNotificationsVisible by
-            lockscreenContentViewModel.areNotificationsVisible.collectAsState()
+            lockscreenContentViewModel.areNotificationsVisible.collectAsStateWithLifecycle()
         val splitShadeTopMargin: Dp =
             if (Flags.centralizedStatusBarHeightFix()) {
                 LargeScreenHeaderHelper.getLargeScreenHeaderHeight(LocalContext.current).dp
@@ -103,6 +106,7 @@
         }
 
         ConstrainedNotificationStack(
+            stackScrollView = stackScrollView.get(),
             viewModel = viewModel,
             modifier =
                 modifier
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
index fc8b3b9..44bda95 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
@@ -26,7 +26,6 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -34,6 +33,7 @@
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
@@ -160,7 +160,7 @@
     private fun Weather(
         modifier: Modifier = Modifier,
     ) {
-        val isVisible by keyguardSmartspaceViewModel.isWeatherVisible.collectAsState()
+        val isVisible by keyguardSmartspaceViewModel.isWeatherVisible.collectAsStateWithLifecycle()
         if (!isVisible) {
             return
         }
@@ -187,7 +187,7 @@
     private fun Date(
         modifier: Modifier = Modifier,
     ) {
-        val isVisible by keyguardSmartspaceViewModel.isDateVisible.collectAsState()
+        val isVisible by keyguardSmartspaceViewModel.isDateVisible.collectAsStateWithLifecycle()
         if (!isVisible) {
             return
         }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index 63c70c9..88b8298 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -25,7 +25,6 @@
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
@@ -33,6 +32,7 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntOffset
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.SceneTransitionLayout
 import com.android.compose.modifiers.thenIf
@@ -62,9 +62,9 @@
     fun DefaultClockLayout(
         modifier: Modifier = Modifier,
     ) {
-        val currentClockLayout by clockViewModel.currentClockLayout.collectAsState()
+        val currentClockLayout by clockViewModel.currentClockLayout.collectAsStateWithLifecycle()
         val hasCustomPositionUpdatedAnimation by
-            clockViewModel.hasCustomPositionUpdatedAnimation.collectAsState()
+            clockViewModel.hasCustomPositionUpdatedAnimation.collectAsStateWithLifecycle()
         val currentScene =
             when (currentClockLayout) {
                 KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_LARGE_CLOCK ->
@@ -133,7 +133,7 @@
     @Composable
     private fun SceneScope.LargeClockWithSmartSpace(shouldOffSetClockToOneHalf: Boolean = false) {
         val burnIn = rememberBurnIn(clockInteractor)
-        val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState()
+        val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsStateWithLifecycle()
 
         LaunchedEffect(isLargeClockVisible) {
             if (isLargeClockVisible) {
@@ -170,8 +170,8 @@
     @Composable
     private fun SceneScope.WeatherLargeClockWithSmartSpace(modifier: Modifier = Modifier) {
         val burnIn = rememberBurnIn(clockInteractor)
-        val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState()
-        val currentClockState = clockViewModel.currentClock.collectAsState()
+        val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsStateWithLifecycle()
+        val currentClockState = clockViewModel.currentClock.collectAsStateWithLifecycle()
 
         LaunchedEffect(isLargeClockVisible) {
             if (isLargeClockVisible) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 01d62a3..cf2e895 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -38,7 +38,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
@@ -64,6 +63,7 @@
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.lerp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.NestedScrollBehavior
 import com.android.compose.animation.scene.SceneScope
@@ -112,7 +112,7 @@
     modifier: Modifier = Modifier,
     isPeekFromBottom: Boolean = false,
 ) {
-    val headsUpHeight = viewModel.headsUpHeight.collectAsState()
+    val headsUpHeight = viewModel.headsUpHeight.collectAsStateWithLifecycle()
 
     Element(
         Notifications.Elements.HeadsUpNotificationPlaceholder,
@@ -138,6 +138,7 @@
 /** Adds the space where notification stack should appear in the scene. */
 @Composable
 fun SceneScope.ConstrainedNotificationStack(
+    stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
     modifier: Modifier = Modifier,
 ) {
@@ -146,6 +147,7 @@
             modifier.onSizeChanged { viewModel.onConstrainedAvailableSpaceChanged(it.height) }
     ) {
         NotificationPlaceholder(
+            stackScrollView = stackScrollView,
             viewModel = viewModel,
             modifier = Modifier.fillMaxSize(),
         )
@@ -178,9 +180,10 @@
         shadeSession.rememberSaveableSession(saver = ScrollState.Saver, key = null) {
             ScrollState(initial = 0)
         }
-    val syntheticScroll = viewModel.syntheticScroll.collectAsState(0f)
-    val isCurrentGestureOverscroll = viewModel.isCurrentGestureOverscroll.collectAsState(false)
-    val expansionFraction by viewModel.expandFraction.collectAsState(0f)
+    val syntheticScroll = viewModel.syntheticScroll.collectAsStateWithLifecycle(0f)
+    val isCurrentGestureOverscroll =
+        viewModel.isCurrentGestureOverscroll.collectAsStateWithLifecycle(false)
+    val expansionFraction by viewModel.expandFraction.collectAsStateWithLifecycle(0f)
 
     val navBarHeight =
         with(density) { WindowInsets.systemBars.asPaddingValues().calculateBottomPadding().toPx() }
@@ -193,7 +196,8 @@
      */
     val stackHeight = remember { mutableIntStateOf(0) }
 
-    val scrimRounding = viewModel.shadeScrimRounding.collectAsState(ShadeScrimRounding())
+    val scrimRounding =
+        viewModel.shadeScrimRounding.collectAsStateWithLifecycle(ShadeScrimRounding())
 
     // the offset for the notifications scrim. Its upper bound is 0, and its lower bound is
     // calculated in minScrimOffset. The scrim is the same height as the screen minus the
@@ -334,6 +338,7 @@
                     .debugBackground(viewModel, DEBUG_BOX_COLOR)
         ) {
             NotificationPlaceholder(
+                stackScrollView = stackScrollView,
                 viewModel = viewModel,
                 modifier =
                     Modifier.verticalNestedScrollToScene(
@@ -390,6 +395,7 @@
 
 @Composable
 private fun SceneScope.NotificationPlaceholder(
+    stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
     modifier: Modifier = Modifier,
 ) {
@@ -408,10 +414,8 @@
                             " bounds=${coordinates.boundsInWindow()}"
                     }
                     // NOTE: positionInWindow.y scrolls off screen, but boundsInWindow.top will not
-                    viewModel.onStackBoundsChanged(
-                        top = positionInWindow.y,
-                        bottom = positionInWindow.y + coordinates.size.height,
-                    )
+                    stackScrollView.setStackTop(positionInWindow.y)
+                    stackScrollView.setStackBottom(positionInWindow.y + coordinates.size.height)
                 }
     ) {
         content {}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
index 73cb72c..b808044 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
@@ -36,7 +36,6 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
 import androidx.compose.ui.Alignment
@@ -46,6 +45,7 @@
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
@@ -64,8 +64,8 @@
     viewModel: PeopleViewModel,
     onResult: (PeopleViewModel.Result) -> Unit,
 ) {
-    val priorityTiles by viewModel.priorityTiles.collectAsState()
-    val recentTiles by viewModel.recentTiles.collectAsState()
+    val priorityTiles by viewModel.priorityTiles.collectAsStateWithLifecycle()
+    val recentTiles by viewModel.recentTiles.collectAsStateWithLifecycle()
 
     // Call [onResult] this activity when the ViewModel tells us so.
     LaunchedEffect(viewModel.result) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 2f241ce..e8da4bd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -44,7 +44,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -69,6 +68,7 @@
 import androidx.compose.ui.unit.sp
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.compose.animation.Expandable
 import com.android.compose.animation.scene.SceneScope
@@ -132,8 +132,8 @@
     val context = LocalContext.current
 
     // Collect alphas as soon as we are composed, even when not visible.
-    val alpha by viewModel.alpha.collectAsState()
-    val backgroundAlpha = viewModel.backgroundAlpha.collectAsState()
+    val alpha by viewModel.alpha.collectAsStateWithLifecycle()
+    val backgroundAlpha = viewModel.backgroundAlpha.collectAsStateWithLifecycle()
 
     var security by remember { mutableStateOf<FooterActionsSecurityButtonViewModel?>(null) }
     var foregroundServices by remember {
@@ -181,7 +181,6 @@
     val horizontalPadding = dimensionResource(R.dimen.qs_content_horizontal_padding)
     Row(
         modifier
-            .sysuiResTag("qs_footer_actions")
             .fillMaxWidth()
             .graphicsLayer { this.alpha = alpha }
             .then(backgroundModifier)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt
index ca6b343..73a624a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt
@@ -21,13 +21,13 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.offset
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.modifiers.height
 import com.android.compose.modifiers.width
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
@@ -40,13 +40,13 @@
     qsSceneAdapter: QSSceneAdapter,
     modifier: Modifier = Modifier,
 ) {
-    val isShowing by viewModel.isShowing.collectAsState()
+    val isShowing by viewModel.isShowing.collectAsStateWithLifecycle()
     val mirrorAlpha by
         animateFloatAsState(
             targetValue = if (isShowing) 1f else 0f,
             label = "alphaAnimationBrightnessMirrorShowing",
         )
-    val mirrorOffsetAndSize by viewModel.locationAndSize.collectAsState()
+    val mirrorOffsetAndSize by viewModel.locationAndSize.collectAsStateWithLifecycle()
     val offset = IntOffset(0, mirrorOffsetAndSize.yOffset)
 
     Box(modifier = modifier.fillMaxSize().graphicsLayer { alpha = mirrorAlpha }) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index 46be6b8..d109988 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -22,18 +22,19 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.MovableElementScenePicker
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.TransitionState
 import com.android.compose.animation.scene.ValueKey
 import com.android.compose.modifiers.thenIf
+import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Companion.Collapsing
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Expanding
@@ -143,7 +144,9 @@
     MovableElement(
         key = QuickSettings.Elements.Content,
         modifier =
-            modifier.fillMaxWidth().layout { measurable, constraints ->
+            modifier.sysuiResTag("quick_settings_panel").fillMaxWidth().layout {
+                measurable,
+                constraints ->
                 val placeable = measurable.measure(constraints)
                 // Use the height of the correct view based on the scene it is being composed in
                 val height = heightProvider().coerceAtLeast(0)
@@ -161,9 +164,11 @@
     state: QSSceneAdapter.State,
     modifier: Modifier = Modifier,
 ) {
-    val qsView by qsSceneAdapter.qsView.collectAsState(null)
+    val qsView by qsSceneAdapter.qsView.collectAsStateWithLifecycle(null)
     val isCustomizing by
-        qsSceneAdapter.isCustomizerShowing.collectAsState(qsSceneAdapter.isCustomizerShowing.value)
+        qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle(
+            qsSceneAdapter.isCustomizerShowing.value
+        )
     QuickSettingsTheme {
         val context = LocalContext.current
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 6ae0efa..d76b19f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -51,7 +51,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
@@ -63,6 +62,7 @@
 import androidx.compose.ui.res.colorResource
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.TransitionState
 import com.android.compose.animation.scene.animateSceneFloatAsState
@@ -163,7 +163,8 @@
 ) {
     val cutoutLocation = LocalDisplayCutout.current.location
 
-    val brightnessMirrorShowing by viewModel.brightnessMirrorViewModel.isShowing.collectAsState()
+    val brightnessMirrorShowing by
+        viewModel.brightnessMirrorViewModel.isShowing.collectAsStateWithLifecycle()
     val contentAlpha by
         animateFloatAsState(
             targetValue = if (brightnessMirrorShowing) 0f else 1f,
@@ -198,10 +199,11 @@
                     Modifier.displayCutoutPadding()
                 },
     ) {
-        val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState()
-        val isCustomizerShowing by viewModel.qsSceneAdapter.isCustomizerShowing.collectAsState()
+        val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle()
+        val isCustomizerShowing by
+            viewModel.qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle()
         val customizingAnimationDuration by
-            viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsState()
+            viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsStateWithLifecycle()
         val screenHeight = LocalRawScreenHeight.current
 
         BackHandler(
@@ -343,10 +345,10 @@
                         viewModel.qsSceneAdapter,
                         { viewModel.qsSceneAdapter.qsHeight },
                         isSplitShade = false,
-                        modifier = Modifier.sysuiResTag("quick_settings_panel")
+                        modifier = Modifier
                     )
 
-                    val isMediaVisible by viewModel.isMediaVisible.collectAsState()
+                    val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
 
                     MediaCarousel(
                         isVisible = isMediaVisible,
@@ -362,7 +364,8 @@
                 isCustomizing = isCustomizing,
                 customizingAnimationDuration = customizingAnimationDuration,
                 lifecycleOwner = lifecycleOwner,
-                modifier = Modifier.align(Alignment.CenterHorizontally),
+                modifier =
+                    Modifier.align(Alignment.CenterHorizontally).sysuiResTag("qs_footer_actions"),
             )
         }
         NotificationScrollingStack(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 7af9b7b..92b2b4e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -23,7 +23,6 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -34,6 +33,7 @@
 import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.motionEventSpy
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayout
@@ -68,8 +68,9 @@
     modifier: Modifier = Modifier,
 ) {
     val coroutineScope = rememberCoroutineScope()
-    val currentSceneKey: SceneKey by viewModel.currentScene.collectAsState()
-    val currentDestinations by viewModel.currentDestinationScenes(coroutineScope).collectAsState()
+    val currentSceneKey: SceneKey by viewModel.currentScene.collectAsStateWithLifecycle()
+    val currentDestinations by
+        viewModel.currentDestinationScenes(coroutineScope).collectAsStateWithLifecycle()
     val state: MutableSceneTransitionLayoutState = remember {
         MutableSceneTransitionLayoutState(
             initialScene = currentSceneKey,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index d528736..00ef11d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -30,13 +30,13 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.ReadOnlyComposable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexScenePicker
 import com.android.compose.animation.scene.SceneScope
@@ -51,7 +51,7 @@
     modifier: Modifier = Modifier,
     content: @Composable () -> Unit,
 ) {
-    val backgroundScene by viewModel.backgroundScene.collectAsState()
+    val backgroundScene by viewModel.backgroundScene.collectAsStateWithLifecycle()
 
     Box(modifier) {
         if (backgroundScene == Scenes.Lockscreen) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 709a416..ac3e015 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -35,7 +35,6 @@
 import androidx.compose.material3.ColorScheme
 import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
@@ -52,6 +51,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.max
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexScenePicker
 import com.android.compose.animation.scene.SceneScope
@@ -118,7 +118,7 @@
     statusBarIconController: StatusBarIconController,
     modifier: Modifier = Modifier,
 ) {
-    val isDisabled by viewModel.isDisabled.collectAsState()
+    val isDisabled by viewModel.isDisabled.collectAsStateWithLifecycle()
     if (isDisabled) {
         return
     }
@@ -138,7 +138,7 @@
             }
         }
 
-    val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
+    val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
 
     // This layout assumes it is globally positioned at (0, 0) and is the
     // same size as the screen.
@@ -271,7 +271,7 @@
     statusBarIconController: StatusBarIconController,
     modifier: Modifier = Modifier,
 ) {
-    val isDisabled by viewModel.isDisabled.collectAsState()
+    val isDisabled by viewModel.isDisabled.collectAsStateWithLifecycle()
     if (isDisabled) {
         return
     }
@@ -280,7 +280,7 @@
         derivedStateOf { shouldUseExpandedFormat(layoutState.transitionState) }
     }
 
-    val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
+    val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
 
     Box(modifier = modifier.sysuiResTag(ShadeHeader.TestTags.Root)) {
         if (isPrivacyChipVisible) {
@@ -435,7 +435,7 @@
     modifier: Modifier = Modifier,
 ) {
     Row(modifier = modifier) {
-        val subIds by viewModel.mobileSubIds.collectAsState()
+        val subIds by viewModel.mobileSubIds.collectAsStateWithLifecycle()
 
         for (subId in subIds) {
             Spacer(modifier = Modifier.width(5.dp))
@@ -472,10 +472,12 @@
     val micSlot = stringResource(id = com.android.internal.R.string.status_bar_microphone)
     val locationSlot = stringResource(id = com.android.internal.R.string.status_bar_location)
 
-    val isSingleCarrier by viewModel.isSingleCarrier.collectAsState()
-    val isPrivacyChipEnabled by viewModel.isPrivacyChipEnabled.collectAsState()
-    val isMicCameraIndicationEnabled by viewModel.isMicCameraIndicationEnabled.collectAsState()
-    val isLocationIndicationEnabled by viewModel.isLocationIndicationEnabled.collectAsState()
+    val isSingleCarrier by viewModel.isSingleCarrier.collectAsStateWithLifecycle()
+    val isPrivacyChipEnabled by viewModel.isPrivacyChipEnabled.collectAsStateWithLifecycle()
+    val isMicCameraIndicationEnabled by
+        viewModel.isMicCameraIndicationEnabled.collectAsStateWithLifecycle()
+    val isLocationIndicationEnabled by
+        viewModel.isLocationIndicationEnabled.collectAsStateWithLifecycle()
 
     AndroidView(
         factory = { context ->
@@ -544,7 +546,7 @@
     viewModel: ShadeHeaderViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val privacyList by viewModel.privacyItems.collectAsState()
+    val privacyList by viewModel.privacyItems.collectAsStateWithLifecycle()
 
     AndroidView(
         factory = { context ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 42e6fcc..a0278a6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -45,7 +45,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -58,6 +57,7 @@
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.res.colorResource
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexScenePicker
 import com.android.compose.animation.scene.SceneScope
@@ -71,6 +71,7 @@
 import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
 import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
 import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
+import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
@@ -177,7 +178,7 @@
     modifier: Modifier = Modifier,
     shadeSession: SaveableSession,
 ) {
-    val shadeMode by viewModel.shadeMode.collectAsState()
+    val shadeMode by viewModel.shadeMode.collectAsStateWithLifecycle()
     when (shadeMode) {
         is ShadeMode.Single ->
             SingleShade(
@@ -228,8 +229,8 @@
             key = QuickSettings.SharedValues.TilesSquishiness,
             canOverflow = false
         )
-    val isClickable by viewModel.isClickable.collectAsState()
-    val isMediaVisible by viewModel.isMediaVisible.collectAsState()
+    val isClickable by viewModel.isClickable.collectAsStateWithLifecycle()
+    val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
 
     val shouldPunchHoleBehindScrim =
         layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade) ||
@@ -335,10 +336,11 @@
 ) {
     val screenCornerRadius = LocalScreenCornerRadius.current
 
-    val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState()
-    val isCustomizerShowing by viewModel.qsSceneAdapter.isCustomizerShowing.collectAsState()
+    val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle()
+    val isCustomizerShowing by
+        viewModel.qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle()
     val customizingAnimationDuration by
-        viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsState()
+        viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsStateWithLifecycle()
     val lifecycleOwner = LocalLifecycleOwner.current
     val footerActionsViewModel =
         remember(lifecycleOwner, viewModel) { viewModel.getFooterActionsViewModel(lifecycleOwner) }
@@ -353,13 +355,13 @@
             .unfoldTranslationX(
                 isOnStartSide = true,
             )
-            .collectAsState(0f)
+            .collectAsStateWithLifecycle(0f)
     val unfoldTranslationXForEndSide by
         viewModel
             .unfoldTranslationX(
                 isOnStartSide = false,
             )
-            .collectAsState(0f)
+            .collectAsStateWithLifecycle(0f)
 
     val navBarBottomHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
     val bottomPadding by
@@ -383,7 +385,8 @@
         }
     }
 
-    val brightnessMirrorShowing by viewModel.brightnessMirrorViewModel.isShowing.collectAsState()
+    val brightnessMirrorShowing by
+        viewModel.brightnessMirrorViewModel.isShowing.collectAsStateWithLifecycle()
     val contentAlpha by
         animateFloatAsState(
             targetValue = if (brightnessMirrorShowing) 0f else 1f,
@@ -393,7 +396,7 @@
     viewModel.notifications.setAlphaForBrightnessMirror(contentAlpha)
     DisposableEffect(Unit) { onDispose { viewModel.notifications.setAlphaForBrightnessMirror(1f) } }
 
-    val isMediaVisible by viewModel.isMediaVisible.collectAsState()
+    val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
 
     val brightnessMirrorShowingModifier = Modifier.graphicsLayer { alpha = contentAlpha }
 
@@ -444,6 +447,7 @@
                         Column(
                             modifier =
                                 Modifier.fillMaxSize()
+                                    .sysuiResTag("expanded_qs_scroll_view")
                                     .weight(1f)
                                     .thenIf(!isCustomizerShowing) {
                                         Modifier.verticalNestedScrollToScene()
@@ -482,6 +486,7 @@
                             lifecycleOwner = lifecycleOwner,
                             modifier =
                                 Modifier.align(Alignment.CenterHorizontally)
+                                    .sysuiResTag("qs_footer_actions")
                                     .then(brightnessMirrorShowingModifier),
                         )
                     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
index 5e107c6..931ff56 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
@@ -3,9 +3,9 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.shadeHeaderText
 import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
 
@@ -14,8 +14,8 @@
     viewModel: ShadeHeaderViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val longerText = viewModel.longerDateText.collectAsState()
-    val shorterText = viewModel.shorterDateText.collectAsState()
+    val longerText = viewModel.longerDateText.collectAsStateWithLifecycle()
+    val shorterText = viewModel.shorterDateText.collectAsStateWithLifecycle()
 
     Layout(
         contents =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
index 44b221c..3976c61 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
@@ -19,31 +19,34 @@
 import android.view.Gravity
 import androidx.compose.foundation.basicMarquee
 import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonColors
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.LiveRegionMode
 import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.semantics.contentDescription
-import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.liveRegion
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.res.R
 import com.android.systemui.volume.panel.component.anc.ui.viewmodel.AncViewModel
 import com.android.systemui.volume.panel.component.popup.ui.composable.VolumePanelPopup
@@ -60,19 +63,12 @@
 
     @Composable
     override fun VolumePanelComposeScope.Content(modifier: Modifier) {
-        val slice by viewModel.buttonSlice.collectAsState()
+        val slice by viewModel.buttonSlice.collectAsStateWithLifecycle()
         val label = stringResource(R.string.volume_panel_noise_control_title)
         val screenWidth: Float =
             with(LocalDensity.current) { LocalConfiguration.current.screenWidthDp.dp.toPx() }
         var gravity by remember { mutableIntStateOf(Gravity.CENTER_HORIZONTAL) }
         val isClickable = viewModel.isClickable(slice)
-        val onClick =
-            if (isClickable) {
-                { with(ancPopup) { show(null, gravity) } }
-            } else {
-                null
-            }
-
         Column(
             modifier =
                 modifier.onGloballyPositioned {
@@ -81,20 +77,33 @@
             verticalArrangement = Arrangement.spacedBy(12.dp),
             horizontalAlignment = Alignment.CenterHorizontally,
         ) {
-            SliceAndroidView(
-                modifier =
-                    Modifier.height(64.dp)
-                        .fillMaxWidth()
-                        .semantics {
-                            role = Role.Button
+            Box(
+                modifier = Modifier.height(64.dp),
+            ) {
+                SliceAndroidView(
+                    modifier = modifier.fillMaxSize(),
+                    slice = slice,
+                    onWidthChanged = viewModel::onButtonSliceWidthChanged,
+                    enableAccessibility = false,
+                )
+                Button(
+                    modifier =
+                        modifier.fillMaxSize().padding(8.dp).semantics {
+                            liveRegion = LiveRegionMode.Polite
                             contentDescription = label
-                        }
-                        .clip(RoundedCornerShape(28.dp)),
-                slice = slice,
-                isEnabled = onClick != null,
-                onWidthChanged = viewModel::onButtonSliceWidthChanged,
-                onClick = onClick,
-            )
+                        },
+                    enabled = isClickable,
+                    onClick = { with(ancPopup) { show(null, gravity) } },
+                    colors =
+                        ButtonColors(
+                            contentColor = Color.Transparent,
+                            containerColor = Color.Transparent,
+                            disabledContentColor = Color.Transparent,
+                            disabledContainerColor = Color.Transparent,
+                        )
+                ) {}
+            }
+
             Text(
                 modifier = Modifier.clearAndSetSemantics {}.basicMarquee(),
                 text = label,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
index d53dbf9..15df1be 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
@@ -23,11 +23,11 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.slice.Slice
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.animation.Expandable
@@ -67,14 +67,14 @@
 
     @Composable
     private fun Content(dialog: SystemUIDialog) {
-        val isAvailable by viewModel.isAvailable.collectAsState(true)
+        val isAvailable by viewModel.isAvailable.collectAsStateWithLifecycle(true)
 
         if (!isAvailable) {
             SideEffect { dialog.dismiss() }
             return
         }
 
-        val slice by viewModel.popupSlice.collectAsState()
+        val slice by viewModel.popupSlice.collectAsStateWithLifecycle()
         SliceAndroidView(
             modifier = Modifier.fillMaxWidth(),
             slice = slice,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
index fc5d212..23d50c5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
@@ -16,11 +16,12 @@
 
 package com.android.systemui.volume.panel.component.anc.ui.composable
 
-import android.annotation.SuppressLint
 import android.content.Context
+import android.os.Bundle
 import android.view.ContextThemeWrapper
-import android.view.MotionEvent
 import android.view.View
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.viewinterop.AndroidView
@@ -32,14 +33,13 @@
 fun SliceAndroidView(
     slice: Slice?,
     modifier: Modifier = Modifier,
-    isEnabled: Boolean = true,
     onWidthChanged: ((Int) -> Unit)? = null,
-    onClick: (() -> Unit)? = null,
+    enableAccessibility: Boolean = true,
 ) {
     AndroidView(
         modifier = modifier,
         factory = { context: Context ->
-            ClickableSliceView(
+            ComposeSliceView(
                     ContextThemeWrapper(context, R.style.Widget_SliceView_VolumePanel),
                 )
                 .apply {
@@ -47,17 +47,18 @@
                     isScrollable = false
                     importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
                     setShowTitleItems(true)
-                    if (onWidthChanged != null) {
-                        addOnLayoutChangeListener(OnWidthChangedLayoutListener(onWidthChanged))
-                    }
                 }
         },
-        update = { sliceView: ClickableSliceView ->
+        update = { sliceView: ComposeSliceView ->
             sliceView.slice = slice
-            sliceView.onClick = onClick
-            sliceView.isEnabled = isEnabled
-            sliceView.isClickable = isEnabled
-        }
+            sliceView.layoutListener = onWidthChanged?.let(::OnWidthChangedLayoutListener)
+            sliceView.enableAccessibility = enableAccessibility
+        },
+        onRelease = { sliceView: ComposeSliceView ->
+            sliceView.layoutListener = null
+            sliceView.slice = null
+            sliceView.enableAccessibility = true
+        },
     )
 }
 
@@ -83,26 +84,39 @@
     }
 }
 
-/**
- * [SliceView] that prioritises [onClick] when its clicked instead of passing the event to the slice
- * first.
- */
-@SuppressLint("ViewConstructor") // only used in this class
-private class ClickableSliceView(context: Context) : SliceView(context) {
+private class ComposeSliceView(context: Context) : SliceView(context) {
 
-    var onClick: (() -> Unit)? = null
+    var enableAccessibility: Boolean = true
+    var layoutListener: OnLayoutChangeListener? = null
+        set(value) {
+            field?.let { removeOnLayoutChangeListener(it) }
+            field = value
+            field?.let { addOnLayoutChangeListener(it) }
+        }
 
-    init {
-        if (onClick != null) {
-            setOnClickListener {}
+    override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) {
+        if (enableAccessibility) {
+            super.onInitializeAccessibilityNodeInfo(info)
         }
     }
 
-    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
-        return (isSliceViewClickable && onClick != null) || super.onInterceptTouchEvent(ev)
+    override fun onInitializeAccessibilityEvent(event: AccessibilityEvent?) {
+        if (enableAccessibility) {
+            super.onInitializeAccessibilityEvent(event)
+        }
     }
 
-    override fun onClick(v: View?) {
-        onClick?.takeIf { isSliceViewClickable }?.let { it() } ?: super.onClick(v)
+    override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
+        return if (enableAccessibility) {
+            super.performAccessibilityAction(action, arguments)
+        } else {
+            false
+        }
+    }
+
+    override fun addChildrenForAccessibility(outChildren: ArrayList<View>?) {
+        if (enableAccessibility) {
+            super.addChildrenForAccessibility(outChildren)
+        }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
index f11c3a5..e1ae80f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
@@ -28,7 +28,6 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
@@ -44,6 +43,7 @@
 import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.Expandable
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.ui.compose.Icon
@@ -61,7 +61,7 @@
 
     @Composable
     override fun VolumePanelComposeScope.Content(modifier: Modifier) {
-        val viewModelByState by viewModelFlow.collectAsState()
+        val viewModelByState by viewModelFlow.collectAsStateWithLifecycle()
         val viewModel = viewModelByState ?: return
         val label = viewModel.label.toString()
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
index 12debbc..1b821d3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
@@ -29,7 +29,6 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -42,6 +41,7 @@
 import androidx.compose.ui.semantics.toggleableState
 import androidx.compose.ui.state.ToggleableState
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
 import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
@@ -56,7 +56,7 @@
 
     @Composable
     override fun VolumePanelComposeScope.Content(modifier: Modifier) {
-        val viewModelByState by viewModelFlow.collectAsState()
+        val viewModelByState by viewModelFlow.collectAsStateWithLifecycle()
         val viewModel = viewModelByState ?: return
         val label = viewModel.label.toString()
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
index ded63a1..237bbfd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
@@ -45,7 +45,6 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -55,6 +54,7 @@
 import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.Expandable
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.common.ui.compose.toColor
@@ -77,9 +77,9 @@
     @Composable
     override fun VolumePanelComposeScope.Content(modifier: Modifier) {
         val connectedDeviceViewModel: ConnectedDeviceViewModel? by
-            viewModel.connectedDeviceViewModel.collectAsState()
+            viewModel.connectedDeviceViewModel.collectAsStateWithLifecycle()
         val deviceIconViewModel: DeviceIconViewModel? by
-            viewModel.deviceIconViewModel.collectAsState()
+            viewModel.deviceIconViewModel.collectAsStateWithLifecycle()
         val clickLabel = stringResource(R.string.volume_panel_enter_media_output_settings)
 
         Expandable(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
index d41acd9..9891b5b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -23,11 +23,11 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.ui.compose.Icon
@@ -72,14 +72,14 @@
 
     @Composable
     private fun Content(dialog: SystemUIDialog) {
-        val isAvailable by viewModel.isAvailable.collectAsState()
+        val isAvailable by viewModel.isAvailable.collectAsStateWithLifecycle()
 
         if (!isAvailable) {
             SideEffect { dialog.dismiss() }
             return
         }
 
-        val enabledModelStates by viewModel.spatialAudioButtons.collectAsState()
+        val enabledModelStates by viewModel.spatialAudioButtons.collectAsStateWithLifecycle()
         if (enabledModelStates.isEmpty()) {
             return
         }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
index 1def7fe..072e91a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
@@ -40,7 +40,6 @@
 import androidx.compose.material3.IconButtonDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -52,6 +51,7 @@
 import androidx.compose.ui.semantics.stateDescription
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformSliderColors
 import com.android.compose.modifiers.padding
 import com.android.systemui.res.R
@@ -84,7 +84,7 @@
             modifier = Modifier.fillMaxWidth(),
         ) {
             val sliderViewModel: SliderViewModel = viewModels.first()
-            val sliderState by viewModels.first().slider.collectAsState()
+            val sliderState by viewModels.first().slider.collectAsStateWithLifecycle()
             val sliderPadding by topSliderPadding(isExpandable)
 
             VolumeSlider(
@@ -119,7 +119,7 @@
                 Column {
                     for (index in 1..viewModels.lastIndex) {
                         val sliderViewModel: SliderViewModel = viewModels[index]
-                        val sliderState by sliderViewModel.slider.collectAsState()
+                        val sliderState by sliderViewModel.slider.collectAsStateWithLifecycle()
                         transition.AnimatedVisibility(
                             modifier = Modifier.padding(top = 16.dp),
                             visible = { it || !isExpandable },
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
index bb17499..d15430f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
@@ -18,9 +18,9 @@
 
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformSliderColors
 import com.android.compose.grid.VerticalGrid
 import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
@@ -39,7 +39,7 @@
         horizontalSpacing = 24.dp,
     ) {
         for (sliderViewModel in viewModels) {
-            val sliderState = sliderViewModel.slider.collectAsState().value
+            val sliderState = sliderViewModel.slider.collectAsStateWithLifecycle().value
             VolumeSlider(
                 modifier = Modifier.fillMaxWidth(),
                 state = sliderState,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
index 79056b2..770c5d5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
@@ -18,9 +18,9 @@
 
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformSliderDefaults
 import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
 import com.android.systemui.volume.panel.component.volume.ui.viewmodel.AudioVolumeComponentViewModel
@@ -38,7 +38,8 @@
 
     @Composable
     override fun VolumePanelComposeScope.Content(modifier: Modifier) {
-        val sliderViewModels: List<SliderViewModel> by viewModel.sliderViewModels.collectAsState()
+        val sliderViewModels: List<SliderViewModel> by
+            viewModel.sliderViewModels.collectAsStateWithLifecycle()
         if (sliderViewModels.isEmpty()) {
             return
         }
@@ -52,7 +53,7 @@
             val expandableViewModel: SlidersExpandableViewModel by
                 viewModel
                     .isExpandable(isPortrait)
-                    .collectAsState(SlidersExpandableViewModel.Unavailable)
+                    .collectAsStateWithLifecycle(SlidersExpandableViewModel.Unavailable)
             if (expandableViewModel is SlidersExpandableViewModel.Unavailable) {
                 return
             }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index a602e25..83b8158 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -24,7 +24,6 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -32,6 +31,7 @@
 import androidx.compose.ui.semantics.paneTitle
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.res.R
 import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
 import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState
@@ -54,8 +54,8 @@
     }
 
     val accessibilityTitle = stringResource(R.string.accessibility_volume_settings)
-    val state: VolumePanelState by viewModel.volumePanelState.collectAsState()
-    val components by viewModel.componentsLayout.collectAsState(null)
+    val state: VolumePanelState by viewModel.volumePanelState.collectAsStateWithLifecycle()
+    val components by viewModel.componentsLayout.collectAsStateWithLifecycle(null)
 
     with(VolumePanelComposeScope(state)) {
         components?.let { componentsState ->
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 47a00f4..624f18d 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -133,7 +133,7 @@
         @ColorInt seed: Int,
         darkTheme: Boolean,
         style: Style
-    ) : this(seed, darkTheme, style, 0.5)
+    ) : this(seed, darkTheme, style, 0.0)
 
     @JvmOverloads
     constructor(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
index 1cdc2b6..407bf4c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
@@ -114,7 +114,7 @@
 
             // Change to media unavailable and notify the listener.
             whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(false)
-            mediaDataListenerCaptor.value.onMediaDataRemoved("key")
+            mediaDataListenerCaptor.value.onMediaDataRemoved("key", false)
             runCurrent()
 
             // Media active now returns false.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index ce7b60e..325a324 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -29,6 +29,7 @@
 import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.settingslib.flags.Flags.FLAG_ALLOW_ALL_WIDGETS_ON_LOCKSCREEN_BY_DEFAULT
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.broadcastDispatcher
@@ -202,6 +203,18 @@
                 .isEqualTo(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD)
         }
 
+    @EnableFlags(FLAG_COMMUNAL_HUB, FLAG_ALLOW_ALL_WIDGETS_ON_LOCKSCREEN_BY_DEFAULT)
+    @Test
+    fun hubShowsAllWidgetsByDefaultWhenFlagEnabled() =
+        testScope.runTest {
+            val setting by collectLastValue(underTest.getWidgetCategories(PRIMARY_USER))
+            assertThat(setting?.categories)
+                .isEqualTo(
+                    AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD +
+                        AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN
+                )
+        }
+
     private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
         whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
             .thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index e3dd9ae..8bfa5cf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.DreamManager;
 import android.content.res.Resources;
 import android.graphics.Region;
 import android.os.Handler;
@@ -45,8 +46,10 @@
 import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
 import com.android.systemui.complication.ComplicationHostViewController;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.BlurUtils;
 
 import kotlinx.coroutines.CoroutineDispatcher;
@@ -115,6 +118,12 @@
     DreamOverlayStateController mStateController;
     @Mock
     KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+    @Mock
+    ShadeInteractor mShadeInteractor;
+    @Mock
+    CommunalInteractor mCommunalInteractor;
+    @Mock
+    private DreamManager mDreamManager;
 
     DreamOverlayContainerViewController mController;
 
@@ -146,7 +155,10 @@
                 mAnimationsController,
                 mStateController,
                 mBouncerlessScrimController,
-                mKeyguardTransitionInteractor);
+                mKeyguardTransitionInteractor,
+                mShadeInteractor,
+                mCommunalInteractor,
+                mDreamManager);
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
index d9224d7..bd3b77a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
@@ -27,12 +27,16 @@
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.bluetooth.mockBroadcastDialogController
+import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.data.repository.mediaDataRepository
 import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
+import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaControlInteractor
 import com.android.systemui.media.controls.domain.pipeline.interactor.mediaControlInteractor
 import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
+import com.android.systemui.media.controls.domain.pipeline.mediaDataProcessor
 import com.android.systemui.media.controls.shared.model.MediaData
 import com.android.systemui.media.controls.util.mediaInstanceId
 import com.android.systemui.media.mediaOutputDialogManager
@@ -211,6 +215,21 @@
             )
     }
 
+    @Test
+    fun removeMediaControl() {
+        val listener = mock<MediaDataProcessor.Listener>()
+        kosmos.mediaDataProcessor.addInternalListener(listener)
+
+        var mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
+        kosmos.mediaDataRepository.addMediaEntry(KEY, mediaData)
+
+        underTest.removeMediaControl(null, instanceId, 0L)
+        kosmos.fakeExecutor.advanceClockToNext()
+        kosmos.fakeExecutor.runAllReady()
+
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
+    }
+
     companion object {
         private const val USER_ID = 0
         private const val KEY = "key"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
new file mode 100644
index 0000000..8cb811d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.service.notification.NotificationListenerService
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.mockNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifPipeline
+import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider
+import com.android.systemui.testKosmos
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationMediaManagerTest : SysuiTestCase() {
+
+    private val KEY = "KEY"
+
+    private val kosmos = testKosmos()
+    private val visibilityProvider = kosmos.notificationVisibilityProvider
+    private val notifPipeline = kosmos.notifPipeline
+    private val notifCollection = kosmos.mockNotifCollection
+    private val dumpManager = kosmos.dumpManager
+    private val mediaDataManager = mock<MediaDataManager>()
+    private val backgroundExecutor = FakeExecutor(FakeSystemClock())
+
+    private var listenerCaptor = argumentCaptor<MediaDataManager.Listener>()
+
+    private lateinit var notificationMediaManager: NotificationMediaManager
+
+    @Before
+    fun setup() {
+        notificationMediaManager =
+            NotificationMediaManager(
+                context,
+                visibilityProvider,
+                notifPipeline,
+                notifCollection,
+                mediaDataManager,
+                dumpManager,
+                backgroundExecutor,
+            )
+
+        verify(mediaDataManager).addListener(listenerCaptor.capture())
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS)
+    fun mediaDataRemoved_userInitiated_dismissNotif() {
+        val notifEntryCaptor = argumentCaptor<NotificationEntry>()
+        val notifEntry = mock<NotificationEntry>()
+        whenever(notifEntry.key).thenReturn(KEY)
+        whenever(notifEntry.ranking).thenReturn(NotificationListenerService.Ranking())
+        whenever(notifPipeline.allNotifs).thenReturn(listOf(notifEntry))
+
+        listenerCaptor.lastValue.onMediaDataRemoved(KEY, true)
+
+        verify(notifCollection).dismissNotification(notifEntryCaptor.capture(), any())
+        assertThat(notifEntryCaptor.lastValue.key).isEqualTo(KEY)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS)
+    fun mediaDataRemoved_notUserInitiated_doesNotDismissNotif() {
+        listenerCaptor.lastValue.onMediaDataRemoved(KEY, false)
+
+        verify(notifCollection, never()).dismissNotification(any(), any())
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS)
+    fun mediaDataRemoved_notUserInitiated_flagOff_dismissNotif() {
+        val notifEntryCaptor = argumentCaptor<NotificationEntry>()
+        val notifEntry = mock<NotificationEntry>()
+        whenever(notifEntry.key).thenReturn(KEY)
+        whenever(notifEntry.ranking).thenReturn(NotificationListenerService.Ranking())
+        whenever(notifPipeline.allNotifs).thenReturn(listOf(notifEntry))
+
+        listenerCaptor.lastValue.onMediaDataRemoved(KEY, false)
+
+        verify(notifCollection).dismissNotification(notifEntryCaptor.capture(), any())
+        assertThat(notifEntryCaptor.lastValue.key).isEqualTo(KEY)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 50ae985..ce134e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -31,9 +31,9 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.compose.animation.scene.ObservableTransitionState;
@@ -57,6 +57,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
+import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.kotlin.JavaAdapter;
@@ -75,7 +76,7 @@
 import org.mockito.verification.VerificationMode;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class VisualStabilityCoordinatorTest extends SysuiTestCase {
 
@@ -86,6 +87,7 @@
     @Mock private WakefulnessLifecycle mWakefulnessLifecycle;
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener;
+    @Mock private SeenNotificationsInteractor mSeenNotificationsInteractor;
     @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private VisibilityLocationProvider mVisibilityLocationProvider;
     @Mock private VisualStabilityProvider mVisualStabilityProvider;
@@ -121,6 +123,7 @@
                 mHeadsUpManager,
                 mShadeAnimationInteractor,
                 mJavaAdapter,
+                mSeenNotificationsInteractor,
                 mStatusBarStateController,
                 mVisibilityLocationProvider,
                 mVisualStabilityProvider,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
index 1f0812d..ee9fd349 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
@@ -44,13 +44,4 @@
                 collectLastValue(kosmos.notificationStackAppearanceInteractor.shadeScrimBounds)
             assertThat(stackBounds).isEqualTo(bounds)
         }
-
-    @Test
-    fun onStackBoundsChanged() =
-        kosmos.testScope.runTest {
-            underTest.onStackBoundsChanged(top = 5f, bottom = 500f)
-            assertThat(kosmos.notificationStackAppearanceInteractor.stackTop.value).isEqualTo(5f)
-            assertThat(kosmos.notificationStackAppearanceInteractor.stackBottom.value)
-                .isEqualTo(500f)
-        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index f2ce745..da17366 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -660,9 +660,6 @@
 
             overrideResource(R.bool.config_use_split_notification_shade, false)
             configurationRepository.onAnyConfigurationChange()
-            keyguardInteractor.setNotificationContainerBounds(
-                NotificationContainerBounds(top = 1f, bottom = 2f)
-            )
 
             assertThat(maxNotifications).isEqualTo(10)
 
@@ -691,9 +688,6 @@
 
             overrideResource(R.bool.config_use_split_notification_shade, false)
             configurationRepository.onAnyConfigurationChange()
-            keyguardInteractor.setNotificationContainerBounds(
-                NotificationContainerBounds(top = 1f, bottom = 2f)
-            )
 
             assertThat(maxNotifications).isEqualTo(10)
 
@@ -728,9 +722,6 @@
 
             overrideResource(R.bool.config_use_split_notification_shade, false)
             configurationRepository.onAnyConfigurationChange()
-            keyguardInteractor.setNotificationContainerBounds(
-                NotificationContainerBounds(top = 1f, bottom = 2f)
-            )
 
             // -1 means No Limit
             assertThat(maxNotifications).isEqualTo(-1)
diff --git a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
index f644584f..5755dcd 100644
--- a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
@@ -47,6 +47,7 @@
         android:layout_gravity="center"
         android:contentDescription="@null"
         android:scaleType="fitXY"
+        android:importantForAccessibility="no"
         app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
         app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
         app:layout_constraintStart_toStartOf="@+id/biometric_icon"
diff --git a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
index 46b8e46..05f6fae 100644
--- a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
@@ -229,6 +229,7 @@
         android:layout_gravity="center"
         android:contentDescription="@null"
         android:scaleType="fitXY"
+        android:importantForAccessibility="no"
         app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
         app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
         app:layout_constraintStart_toStartOf="@+id/biometric_icon"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
index d51fe58..fa4d9a8 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
@@ -222,6 +222,7 @@
         android:layout_gravity="center"
         android:contentDescription="@null"
         android:scaleType="fitXY"
+        android:importantForAccessibility="no"
         app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
         app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
         app:layout_constraintStart_toStartOf="@+id/biometric_icon"
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
index c1be37a..a51d8ff 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
@@ -23,7 +23,6 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
@@ -32,6 +31,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformSlider
 import com.android.systemui.brightness.shared.GammaBrightness
 import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
@@ -107,10 +107,13 @@
     viewModel: BrightnessSliderViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val gamma: Int by viewModel.currentBrightness.map { it.value }.collectAsState(initial = 0)
+    val gamma: Int by
+        viewModel.currentBrightness.map { it.value }.collectAsStateWithLifecycle(initialValue = 0)
     val coroutineScope = rememberCoroutineScope()
     val restriction by
-        viewModel.policyRestriction.collectAsState(initial = PolicyRestriction.NoRestriction)
+        viewModel.policyRestriction.collectAsStateWithLifecycle(
+            initialValue = PolicyRestriction.NoRestriction
+        )
 
     BrightnessSlider(
         gammaValue = gamma,
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 5653bc2..2eca02c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -31,9 +31,12 @@
 import com.android.systemui.communal.domain.interactor.CommunalInteractor;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -88,6 +91,8 @@
     private final JavaAdapter mJavaAdapter;
     private final SystemClock mSystemClock;
     private final Lazy<SelectedUserInteractor> mUserInteractor;
+    private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractor;
+    private final Lazy<SceneContainerOcclusionInteractor> mSceneContainerOcclusionInteractor;
 
     private int mState;
     private boolean mShowingAod;
@@ -170,7 +175,9 @@
             JavaAdapter javaAdapter,
             SystemClock systemClock,
             Lazy<SelectedUserInteractor> userInteractor,
-            Lazy<CommunalInteractor> communalInteractorLazy) {
+            Lazy<CommunalInteractor> communalInteractorLazy,
+            Lazy<DeviceEntryInteractor> deviceEntryInteractor,
+            Lazy<SceneContainerOcclusionInteractor> sceneContainerOcclusionInteractor) {
         mFalsingDataProvider = falsingDataProvider;
         mFalsingManager = falsingManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -186,6 +193,8 @@
         mSystemClock = systemClock;
         mUserInteractor = userInteractor;
         mCommunalInteractorLazy = communalInteractorLazy;
+        mDeviceEntryInteractor = deviceEntryInteractor;
+        mSceneContainerOcclusionInteractor = sceneContainerOcclusionInteractor;
     }
 
     @Override
@@ -196,7 +205,18 @@
         mStatusBarStateController.addCallback(mStatusBarStateListener);
         mState = mStatusBarStateController.getState();
 
-        mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
+        if (SceneContainerFlag.isEnabled()) {
+            mJavaAdapter.alwaysCollectFlow(
+                    mDeviceEntryInteractor.get().isDeviceEntered(),
+                    this::isDeviceEnteredChanged
+            );
+            mJavaAdapter.alwaysCollectFlow(
+                    mSceneContainerOcclusionInteractor.get().getInvisibleDueToOcclusion(),
+                    this::isInvisibleDueToOcclusionChanged
+            );
+        } else {
+            mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
+        }
 
         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
 
@@ -216,6 +236,14 @@
         mDockManager.addListener(mDockEventListener);
     }
 
+    public void isDeviceEnteredChanged(boolean unused) {
+        updateSensorRegistration();
+    }
+
+    public void isInvisibleDueToOcclusionChanged(boolean unused) {
+        updateSensorRegistration();
+    }
+
     @Override
     public void onSuccessfulUnlock() {
         logDebug("REAL: onSuccessfulUnlock");
@@ -302,7 +330,7 @@
     @Override
     public void onTouchEvent(MotionEvent ev) {
         logDebug("REAL: onTouchEvent(" + MotionEvent.actionToString(ev.getActionMasked()) + ")");
-        if (!mKeyguardStateController.isShowing()) {
+        if (!isKeyguardShowing()) {
             avoidGesture();
             return;
         }
@@ -402,8 +430,8 @@
         final boolean isKeyguard = mState == StatusBarState.KEYGUARD;
 
         final boolean isShadeOverOccludedKeyguard = mState == StatusBarState.SHADE
-                && mKeyguardStateController.isShowing()
-                && mKeyguardStateController.isOccluded();
+                && isKeyguardShowing()
+                && isKeyguardOccluded();
 
         return mScreenOn && !mShowingAod && (isKeyguard || isShadeOverOccludedKeyguard);
     }
@@ -447,6 +475,32 @@
         mFalsingManager.onProximityEvent(new ProximityEventImpl(proximityEvent));
     }
 
+    /**
+     * Returns {@code true} if the keyguard is showing (whether or not the screen is on, whether or
+     * not an activity is occluding the keyguard, and whether or not the shade is open on top of the
+     * keyguard), or {@code false} if the user has dismissed the keyguard by authenticating or
+     * swiping up.
+     */
+    private boolean isKeyguardShowing() {
+        if (SceneContainerFlag.isEnabled()) {
+            return !mDeviceEntryInteractor.get().isDeviceEntered().getValue();
+        } else {
+            return mKeyguardStateController.isShowing();
+        }
+    }
+
+    /**
+     * Returns {@code true} if there is an activity display on top of ("occluding") the keyguard, or
+     * {@code false} if an activity is not occluding the keyguard (including if the keyguard is not
+     * showing at all).
+     */
+    private boolean isKeyguardOccluded() {
+        if (SceneContainerFlag.isEnabled()) {
+            return mSceneContainerOcclusionInteractor.get().getInvisibleDueToOcclusion().getValue();
+        } else {
+            return mKeyguardStateController.isOccluded();
+        }
+    }
 
     static void logDebug(String msg) {
         if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
index 8efc66d..a2afcac 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
@@ -66,11 +66,11 @@
 import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance;
 import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel;
 
-import java.util.ArrayList;
-
 import kotlin.Unit;
 import kotlin.jvm.functions.Function0;
 
+import java.util.ArrayList;
+
 /**
  * Handles the visual elements and animations for the clipboard overlay.
  */
@@ -109,6 +109,7 @@
     private View mDismissButton;
     private LinearLayout mActionContainer;
     private ClipboardOverlayCallbacks mClipboardCallbacks;
+    private ActionButtonViewBinder mActionButtonViewBinder = new ActionButtonViewBinder();
 
     public ClipboardOverlayView(Context context) {
         this(context, null);
@@ -152,7 +153,7 @@
 
     private void bindDefaultActionChips() {
         if (screenshotShelfUi2()) {
-            ActionButtonViewBinder.INSTANCE.bind(mRemoteCopyChip,
+            mActionButtonViewBinder.bind(mRemoteCopyChip,
                     ActionButtonViewModel.Companion.withNextId(
                             new ActionButtonAppearance(
                                     Icon.createWithResource(mContext,
@@ -169,7 +170,7 @@
                                     return null;
                                 }
                             }));
-            ActionButtonViewBinder.INSTANCE.bind(mShareChip,
+            mActionButtonViewBinder.bind(mShareChip,
                     ActionButtonViewModel.Companion.withNextId(
                             new ActionButtonAppearance(
                                     Icon.createWithResource(mContext,
@@ -512,7 +513,7 @@
     private View constructShelfActionChip(RemoteAction action, Runnable onFinish) {
         View chip = LayoutInflater.from(mContext).inflate(
                 R.layout.shelf_action_chip, mActionContainer, false);
-        ActionButtonViewBinder.INSTANCE.bind(chip, ActionButtonViewModel.Companion.withNextId(
+        mActionButtonViewBinder.bind(chip, ActionButtonViewModel.Companion.withNextId(
                 new ActionButtonAppearance(action.getIcon().loadDrawable(mContext),
                         action.getTitle(), action.getTitle()), new Function0<>() {
                     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index f437032..971ab11 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.communal
 
 import android.provider.Settings
-import android.service.dreams.Flags.dreamTracksFocus
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.CoreStartable
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
@@ -143,12 +142,10 @@
                 }
         }
 
-        if (dreamTracksFocus()) {
-            bgScope.launch {
-                communalInteractor.isIdleOnCommunal.collectLatest {
-                    withContext(mainDispatcher) {
-                        notificationShadeWindowController.setGlanceableHubShowing(it)
-                    }
+        bgScope.launch {
+            communalInteractor.isIdleOnCommunal.collectLatest {
+                withContext(mainDispatcher) {
+                    notificationShadeWindowController.setGlanceableHubShowing(it)
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
index 03f54c8..5cd15f2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
@@ -17,15 +17,23 @@
 package com.android.systemui.communal.data.model
 
 import android.appwidget.AppWidgetProviderInfo
+import com.android.settingslib.flags.Flags.allowAllWidgetsOnLockscreenByDefault
 
 /**
  * The widget categories to display on communal hub (where categories is a bitfield with values that
  * match those in {@link AppWidgetProviderInfo}).
  */
 @JvmInline
-value class CommunalWidgetCategories(
-    // The default is keyguard widgets.
-    val categories: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD
-) {
+value class CommunalWidgetCategories(val categories: Int = defaultCategories) {
     fun contains(category: Int) = (categories and category) == category
+
+    companion object {
+        val defaultCategories: Int
+            get() {
+                return AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD or
+                    if (allowAllWidgetsOnLockscreenByDefault())
+                        AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN
+                    else 0
+            }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
index e2fed6d..e5a0e50 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -53,7 +53,7 @@
                 updateMediaModel(data)
             }
 
-            override fun onMediaDataRemoved(key: String) {
+            override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
                 updateMediaModel()
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 9debe0e..88cb64c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -18,7 +18,6 @@
 
 import android.app.admin.DevicePolicyManager
 import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
-import android.appwidget.AppWidgetProviderInfo
 import android.content.IntentFilter
 import android.content.pm.UserInfo
 import android.provider.Settings
@@ -108,10 +107,9 @@
             .onStart { emit(Unit) }
             .map {
                 CommunalWidgetCategories(
-                    // The default is to show only keyguard widgets.
                     secureSettings.getIntForUser(
                         GLANCEABLE_HUB_CONTENT_SETTING,
-                        AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
+                        CommunalWidgetCategories.defaultCategories,
                         user.id
                     )
                 )
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index f9de609..3e5126a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -75,7 +75,7 @@
                 scope = bgScope,
                 // Start this eagerly since the value can be accessed synchronously.
                 started = SharingStarted.Eagerly,
-                initialValue = CommunalWidgetCategories().categories
+                initialValue = CommunalWidgetCategories.defaultCategories
             )
 
     private val workProfileUserInfoCallbackFlow: Flow<UserInfo?> = conflatedCallbackFlow {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 6e04339..60006c6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.dreams;
 
+import static android.service.dreams.Flags.dreamHandlesBeingObscured;
+
 import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress;
 import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamAlphaScaledExpansion;
 import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamYPositionScaledExpansion;
@@ -23,8 +25,10 @@
 import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP;
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
 
 import android.animation.Animator;
+import android.app.DreamManager;
 import android.content.res.Resources;
 import android.graphics.Region;
 import android.os.Handler;
@@ -37,7 +41,9 @@
 import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
 import com.android.systemui.complication.ComplicationHostViewController;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
 import com.android.systemui.dreams.dagger.DreamOverlayModule;
@@ -45,10 +51,12 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.res.R;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.util.ViewController;
 
 import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.flow.FlowKt;
 
 import java.util.Arrays;
 
@@ -68,6 +76,8 @@
     private final DreamOverlayStateController mStateController;
     private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+    private final ShadeInteractor mShadeInteractor;
+    private final CommunalInteractor mCommunalInteractor;
 
     private final ComplicationHostViewController mComplicationHostViewController;
 
@@ -87,9 +97,10 @@
 
     // Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
     private final Handler mHandler;
-    private final CoroutineDispatcher mMainDispatcher;
+    private final CoroutineDispatcher mBackgroundDispatcher;
     private final int mDreamOverlayMaxTranslationY;
     private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
+    private final DreamManager mDreamManager;
 
     private long mJitterStartTimeMillis;
 
@@ -178,7 +189,7 @@
             LowLightTransitionCoordinator lowLightTransitionCoordinator,
             BlurUtils blurUtils,
             @Main Handler handler,
-            @Main CoroutineDispatcher mainDispatcher,
+            @Background CoroutineDispatcher backgroundDispatcher,
             @Main Resources resources,
             @Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset,
             @Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
@@ -188,18 +199,23 @@
             DreamOverlayAnimationsController animationsController,
             DreamOverlayStateController stateController,
             BouncerlessScrimController bouncerlessScrimController,
-            KeyguardTransitionInteractor keyguardTransitionInteractor) {
+            KeyguardTransitionInteractor keyguardTransitionInteractor,
+            ShadeInteractor shadeInteractor,
+            CommunalInteractor communalInteractor,
+            DreamManager dreamManager) {
         super(containerView);
         mDreamOverlayContentView = contentView;
         mStatusBarViewController = statusBarViewController;
         mBlurUtils = blurUtils;
         mDreamOverlayAnimationsController = animationsController;
         mStateController = stateController;
+        mCommunalInteractor = communalInteractor;
         mLowLightTransitionCoordinator = lowLightTransitionCoordinator;
 
         mBouncerlessScrimController = bouncerlessScrimController;
 
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+        mShadeInteractor = shadeInteractor;
 
         mComplicationHostViewController = complicationHostViewController;
         mDreamOverlayMaxTranslationY = resources.getDimensionPixelSize(
@@ -211,11 +227,12 @@
                         ViewGroup.LayoutParams.MATCH_PARENT));
 
         mHandler = handler;
-        mMainDispatcher = mainDispatcher;
+        mBackgroundDispatcher = backgroundDispatcher;
         mMaxBurnInOffset = maxBurnInOffset;
         mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval;
         mMillisUntilFullJitter = millisUntilFullJitter;
         mPrimaryBouncerCallbackInteractor = primaryBouncerCallbackInteractor;
+        mDreamManager = dreamManager;
     }
 
     @Override
@@ -238,11 +255,21 @@
         mView.getRootSurfaceControl().setTouchableRegion(emptyRegion);
         emptyRegion.recycle();
 
-        collectFlow(
-                mView,
-                mKeyguardTransitionInteractor.isFinishedInStateWhere(KeyguardState::isBouncerState),
-                isFinished -> mAnyBouncerShowing = isFinished,
-                mMainDispatcher);
+        if (dreamHandlesBeingObscured()) {
+            collectFlow(
+                    mView,
+                    FlowKt.distinctUntilChanged(combineFlows(
+                            mKeyguardTransitionInteractor.isFinishedInStateWhere(
+                                    KeyguardState::isBouncerState),
+                            mShadeInteractor.isAnyExpanded(),
+                            mCommunalInteractor.isCommunalShowing(),
+                            (anyBouncerShowing, shadeExpanded, communalShowing) -> {
+                                mAnyBouncerShowing = anyBouncerShowing;
+                                return anyBouncerShowing || shadeExpanded || communalShowing;
+                            })),
+                    mDreamManager::setDreamIsObscured,
+                    mBackgroundDispatcher);
+        }
 
         // Start dream entry animations. Skip animations for low light clock.
         if (!mStateController.isLowLightActive()) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index dbaa297..68a252b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -37,6 +37,7 @@
 import android.service.notification.ZenModeConfig;
 import android.text.TextUtils;
 import android.text.style.StyleSpan;
+import android.util.Log;
 
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
@@ -212,21 +213,27 @@
     @AnyThread
     @Override
     public Slice onBindSlice(Uri sliceUri) {
-        Trace.beginSection("KeyguardSliceProvider#onBindSlice");
-        Slice slice;
-        synchronized (this) {
-            ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
-            if (needsMediaLocked()) {
-                addMediaLocked(builder);
-            } else {
-                builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
+        Slice slice = null;
+        try {
+            Trace.beginSection("KeyguardSliceProvider#onBindSlice");
+            synchronized (this) {
+                ListBuilder builder = new ListBuilder(getContext(), mSliceUri,
+                        ListBuilder.INFINITY);
+                if (needsMediaLocked()) {
+                    addMediaLocked(builder);
+                } else {
+                    builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
+                }
+                addNextAlarmLocked(builder);
+                addZenModeLocked(builder);
+                addPrimaryActionLocked(builder);
+                slice = builder.build();
             }
-            addNextAlarmLocked(builder);
-            addZenModeLocked(builder);
-            addPrimaryActionLocked(builder);
-            slice = builder.build();
+        } catch (IllegalStateException e) {
+            Log.w(TAG, "Could not initialize slice", e);
+        } finally {
+            Trace.endSection();
         }
-        Trace.endSection();
         return slice;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 2d7b737..7285739 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -97,6 +97,7 @@
 
     /** Bounds of the notification container. */
     val notificationContainerBounds: StateFlow<NotificationContainerBounds> by lazy {
+        SceneContainerFlag.assertInLegacyMode()
         combine(
                 _notificationPlaceholderBounds,
                 sharedNotificationContainerInteractor.get().configurationBasedDimensions,
@@ -115,6 +116,7 @@
     }
 
     fun setNotificationContainerBounds(position: NotificationContainerBounds) {
+        SceneContainerFlag.assertInLegacyMode()
         _notificationPlaceholderBounds.value = position
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 30c6718..8f9a70980 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -218,6 +218,8 @@
             .map { step -> step.to }
             .shareIn(scope, SharingStarted.Eagerly, replay = 1)
 
+    val currentTransitionInfo: StateFlow<TransitionInfo> = repository.currentTransitionInfoInternal
+
     /** The from state of the last [TransitionState.STARTED] transition. */
     // TODO: is it performant to have several SharedFlows side by side instead of one?
     @SuppressLint("SharedFlowCreation")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index 6e00aa7..3baeb76 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -131,7 +131,7 @@
         val newTransition =
             TransitionInfo(
                 ownerName = this::class.java.simpleName,
-                from = transitionInteractor.getStartedState(),
+                from = transitionInteractor.currentTransitionInfo.value.to,
                 to = state,
                 animator = null,
                 modeOnCanceled = TransitionModeOnCanceled.REVERSE
@@ -150,7 +150,7 @@
     private suspend fun handleTransition(transition: ObservableTransitionState.Transition) {
         if (transition.fromScene == Scenes.Lockscreen) {
             if (currentTransitionId != null) {
-                val currentToState = transitionInteractor.getStartedState()
+                val currentToState = transitionInteractor.currentTransitionInfo.value.to
                 if (currentToState == UNDEFINED) {
                     transitionKtfTo(transitionInteractor.getStartedFromState())
                 }
@@ -169,6 +169,8 @@
     }
 
     private suspend fun transitionKtfTo(state: KeyguardState) {
+        // TODO(b/330311871): This is based on a sharedFlow and thus might not be up-to-date and
+        //  cause a race condition. (There is no known scenario that is currently affected.)
         val currentTransition = transitionInteractor.transitionState.value
         if (currentTransition.isFinishedIn(state)) {
             // This is already the state we want to be in
@@ -199,7 +201,7 @@
     }
 
     private suspend fun startTransitionFromLockscreen() {
-        val currentState = transitionInteractor.getStartedState()
+        val currentState = transitionInteractor.currentTransitionInfo.value.to
         val newTransition =
             TransitionInfo(
                 ownerName = this::class.java.simpleName,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index 218967c..7c745bc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -57,9 +57,9 @@
         addTransition(SmartspaceMoveTransition(config, clockViewModel))
     }
 
-    open class VisibilityBoundsTransition() : Transition() {
-        var captureSmartspace: Boolean = false
-
+    abstract class VisibilityBoundsTransition() : Transition() {
+        abstract val captureSmartspace: Boolean
+        protected val TAG = this::class.simpleName!!
         override fun captureEndValues(transition: TransitionValues) = captureValues(transition)
         override fun captureStartValues(transition: TransitionValues) = captureValues(transition)
         override fun getTransitionProperties(): Array<String> = TRANSITION_PROPERTIES
@@ -76,7 +76,7 @@
                 parent.findViewById<View>(sharedR.id.bc_smartspace_view)
                     ?: parent.findViewById<View>(R.id.keyguard_slice_view)
             if (targetSSView == null) {
-                Log.e(TAG, "Failed to find smartspace equivalent target for animation")
+                Log.e(TAG, "Failed to find smartspace equivalent target under $parent")
                 return
             }
             transition.values[SMARTSPACE_BOUNDS] = targetSSView.getRect()
@@ -109,14 +109,12 @@
             var fromIsVis = fromVis == View.VISIBLE
             var fromAlpha = startValues.values[PROP_ALPHA] as Float
             val fromBounds = startValues.values[PROP_BOUNDS] as Rect
-            val fromSSBounds =
-                if (captureSmartspace) startValues.values[SMARTSPACE_BOUNDS] as Rect else null
+            val fromSSBounds = startValues.values[SMARTSPACE_BOUNDS] as Rect?
 
             val toView = endValues.view
             val toVis = endValues.values[PROP_VISIBILITY] as Int
             val toBounds = endValues.values[PROP_BOUNDS] as Rect
-            val toSSBounds =
-                if (captureSmartspace) endValues.values[SMARTSPACE_BOUNDS] as Rect else null
+            val toSSBounds = endValues.values[SMARTSPACE_BOUNDS] as Rect?
             val toIsVis = toVis == View.VISIBLE
             val toAlpha = if (toIsVis) 1f else 0f
 
@@ -221,9 +219,6 @@
             private const val SMARTSPACE_BOUNDS = "ClockSizeTransition:SSBounds"
             private val TRANSITION_PROPERTIES =
                 arrayOf(PROP_VISIBILITY, PROP_ALPHA, PROP_BOUNDS, SMARTSPACE_BOUNDS)
-
-            private val DEBUG = false
-            private val TAG = VisibilityBoundsTransition::class.simpleName!!
         }
     }
 
@@ -232,18 +227,24 @@
         val viewModel: KeyguardClockViewModel,
         val smartspaceViewModel: KeyguardSmartspaceViewModel,
     ) : VisibilityBoundsTransition() {
+        override val captureSmartspace = !viewModel.isLargeClockVisible.value
+
         init {
             duration = CLOCK_IN_MILLIS
             startDelay = CLOCK_IN_START_DELAY_MILLIS
             interpolator = CLOCK_IN_INTERPOLATOR
-            captureSmartspace =
-                !viewModel.isLargeClockVisible.value && smartspaceViewModel.isSmartspaceEnabled
 
             if (viewModel.isLargeClockVisible.value) {
                 viewModel.currentClock.value?.let {
+                    if (DEBUG) Log.i(TAG, "Large Clock In: ${it.largeClock.layout.views}")
                     it.largeClock.layout.views.forEach { addTarget(it) }
                 }
+                    ?: run {
+                        Log.e(TAG, "No large clock set, falling back")
+                        addTarget(R.id.lockscreen_clock_view_large)
+                    }
             } else {
+                if (DEBUG) Log.i(TAG, "Small Clock In")
                 addTarget(R.id.lockscreen_clock_view)
             }
         }
@@ -282,7 +283,6 @@
             val CLOCK_IN_INTERPOLATOR = Interpolators.LINEAR_OUT_SLOW_IN
             const val SMALL_CLOCK_IN_MOVE_SCALE =
                 CLOCK_IN_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_DOWN_MILLIS.toFloat()
-            private val TAG = ClockFaceInTransition::class.simpleName!!
         }
     }
 
@@ -291,18 +291,24 @@
         val viewModel: KeyguardClockViewModel,
         val smartspaceViewModel: KeyguardSmartspaceViewModel,
     ) : VisibilityBoundsTransition() {
+        override val captureSmartspace = viewModel.isLargeClockVisible.value
+
         init {
             duration = CLOCK_OUT_MILLIS
             interpolator = CLOCK_OUT_INTERPOLATOR
-            captureSmartspace =
-                viewModel.isLargeClockVisible.value && smartspaceViewModel.isSmartspaceEnabled
 
             if (viewModel.isLargeClockVisible.value) {
+                if (DEBUG) Log.i(TAG, "Small Clock Out")
                 addTarget(R.id.lockscreen_clock_view)
             } else {
                 viewModel.currentClock.value?.let {
+                    if (DEBUG) Log.i(TAG, "Large Clock Out: ${it.largeClock.layout.views}")
                     it.largeClock.layout.views.forEach { addTarget(it) }
                 }
+                    ?: run {
+                        Log.e(TAG, "No large clock set, falling back")
+                        addTarget(R.id.lockscreen_clock_view_large)
+                    }
             }
         }
 
@@ -339,7 +345,6 @@
             val CLOCK_OUT_INTERPOLATOR = Interpolators.LINEAR
             const val SMALL_CLOCK_OUT_MOVE_SCALE =
                 CLOCK_OUT_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_UP_MILLIS.toFloat()
-            private val TAG = ClockFaceOutTransition::class.simpleName!!
         }
     }
 
@@ -348,6 +353,8 @@
         val config: IntraBlueprintTransition.Config,
         viewModel: KeyguardClockViewModel,
     ) : VisibilityBoundsTransition() {
+        override val captureSmartspace = false
+
         init {
             duration =
                 if (viewModel.isLargeClockVisible.value) STATUS_AREA_MOVE_UP_MILLIS
@@ -367,4 +374,8 @@
             const val STATUS_AREA_MOVE_DOWN_MILLIS = 467L
         }
     }
+
+    companion object {
+        val DEBUG = true
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImpl.kt
index c02478b..96ef7d2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImpl.kt
@@ -206,11 +206,11 @@
         listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
     }
 
-    override fun onMediaDataRemoved(key: String) {
+    override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
         allEntries.remove(key)
         userEntries.remove(key)?.let {
             // Only notify listeners if something actually changed
-            listeners.forEach { it.onMediaDataRemoved(key) }
+            listeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
         }
     }
 
@@ -246,7 +246,7 @@
                 // Only remove media when the profile is unavailable.
                 if (DEBUG) Log.d(TAG, "Removing $key after profile change")
                 userEntries.remove(key, data)
-                listeners.forEach { listener -> listener.onMediaDataRemoved(key) }
+                listeners.forEach { listener -> listener.onMediaDataRemoved(key, false) }
             }
         }
     }
@@ -261,7 +261,7 @@
         userEntries.clear()
         keyCopy.forEach {
             if (DEBUG) Log.d(TAG, "Removing $it after user change")
-            listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it) }
+            listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it, false) }
         }
 
         allEntries.forEach { (key, data) ->
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
index 3a831156..143d66b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
@@ -545,8 +545,8 @@
      * External listeners registered with [addListener] will be notified after the event propagates
      * through the internal listener pipeline.
      */
-    private fun notifyMediaDataRemoved(key: String) {
-        internalListeners.forEach { it.onMediaDataRemoved(key) }
+    private fun notifyMediaDataRemoved(key: String, userInitiated: Boolean = false) {
+        internalListeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
     }
 
     /**
@@ -578,7 +578,7 @@
             if (it.active == !timedOut && !forceUpdate) {
                 if (it.resumption) {
                     if (DEBUG) Log.d(TAG, "timing out resume player $key")
-                    dismissMediaData(key, 0L /* delay */)
+                    dismissMediaData(key, delay = 0L, userInitiated = false)
                 }
                 return
             }
@@ -627,17 +627,17 @@
         }
     }
 
-    private fun removeEntry(key: String, logEvent: Boolean = true) {
+    private fun removeEntry(key: String, logEvent: Boolean = true, userInitiated: Boolean = false) {
         mediaEntries.remove(key)?.let {
             if (logEvent) {
                 logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
             }
         }
-        notifyMediaDataRemoved(key)
+        notifyMediaDataRemoved(key, userInitiated)
     }
 
     /** Dismiss a media entry. Returns false if the key was not found. */
-    override fun dismissMediaData(key: String, delay: Long): Boolean {
+    override fun dismissMediaData(key: String, delay: Long, userInitiated: Boolean): Boolean {
         val existed = mediaEntries[key] != null
         backgroundExecutor.execute {
             mediaEntries[key]?.let { mediaData ->
@@ -649,7 +649,10 @@
                 }
             }
         }
-        foregroundExecutor.executeDelayed({ removeEntry(key) }, delay)
+        foregroundExecutor.executeDelayed(
+            { removeEntry(key = key, userInitiated = userInitiated) },
+            delay
+        )
         return existed
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
index ad70db5..88910f9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
@@ -53,8 +53,8 @@
         listeners.toSet().forEach { it.onSmartspaceMediaDataLoaded(key, data) }
     }
 
-    override fun onMediaDataRemoved(key: String) {
-        remove(key)
+    override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
+        remove(key, userInitiated)
     }
 
     override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
@@ -71,8 +71,8 @@
         }
     }
 
-    override fun onKeyRemoved(key: String) {
-        remove(key)
+    override fun onKeyRemoved(key: String, userInitiated: Boolean) {
+        remove(key, userInitiated)
     }
 
     /**
@@ -92,10 +92,10 @@
         }
     }
 
-    private fun remove(key: String) {
+    private fun remove(key: String, userInitiated: Boolean) {
         entries.remove(key)?.let {
             val listenersCopy = listeners.toSet()
-            listenersCopy.forEach { it.onMediaDataRemoved(key) }
+            listenersCopy.forEach { it.onMediaDataRemoved(key, userInitiated) }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
index 5432a18..8d19ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
@@ -213,7 +213,7 @@
         listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
     }
 
-    override fun onMediaDataRemoved(key: String) {
+    override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
         mediaFilterRepository.removeMediaEntry(key)?.let { mediaData ->
             val instanceId = mediaData.instanceId
             mediaFilterRepository.removeSelectedUserMediaEntry(instanceId)?.let {
@@ -221,7 +221,7 @@
                     MediaDataLoadingModel.Removed(instanceId)
                 )
                 // Only notify listeners if something actually changed
-                listeners.forEach { it.onMediaDataRemoved(key) }
+                listeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
             }
         }
     }
@@ -270,7 +270,7 @@
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Removed(data.instanceId)
                 )
-                listeners.forEach { listener -> listener.onMediaDataRemoved(key) }
+                listeners.forEach { listener -> listener.onMediaDataRemoved(key, false) }
             }
         }
     }
@@ -288,7 +288,7 @@
                 MediaDataLoadingModel.Removed(instanceId)
             )
             getKey(instanceId)?.let {
-                listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it) }
+                listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it, false) }
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
index 2331aa21..8099e59 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
@@ -60,7 +60,7 @@
     )
 
     /** Dismiss a media entry. Returns false if the key was not found. */
-    fun dismissMediaData(key: String, delay: Long): Boolean
+    fun dismissMediaData(key: String, delay: Long, userInitiated: Boolean): Boolean
 
     /**
      * Called whenever the recommendation has been expired or removed by the user. This will remove
@@ -136,7 +136,7 @@
         ) {}
 
         /** Called whenever a previously existing Media notification was removed. */
-        override fun onMediaDataRemoved(key: String) {}
+        override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {}
 
         /**
          * Called whenever a previously existing Smartspace media data was removed.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
index 1d7c025..eed7752 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
@@ -498,8 +498,8 @@
      * External listeners registered with [MediaCarouselInteractor.addListener] will be notified
      * after the event propagates through the internal listener pipeline.
      */
-    private fun notifyMediaDataRemoved(key: String) {
-        internalListeners.forEach { it.onMediaDataRemoved(key) }
+    private fun notifyMediaDataRemoved(key: String, userInitiated: Boolean = false) {
+        internalListeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
     }
 
     /**
@@ -531,7 +531,7 @@
             if (it.active == !timedOut && !forceUpdate) {
                 if (it.resumption) {
                     if (DEBUG) Log.d(TAG, "timing out resume player $key")
-                    dismissMediaData(key, 0L /* delay */)
+                    dismissMediaData(key, delayMs = 0L, userInitiated = false)
                 }
                 return
             }
@@ -580,17 +580,17 @@
         }
     }
 
-    private fun removeEntry(key: String, logEvent: Boolean = true) {
+    private fun removeEntry(key: String, logEvent: Boolean = true, userInitiated: Boolean = false) {
         mediaDataRepository.removeMediaEntry(key)?.let {
             if (logEvent) {
                 logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
             }
         }
-        notifyMediaDataRemoved(key)
+        notifyMediaDataRemoved(key, userInitiated)
     }
 
     /** Dismiss a media entry. Returns false if the key was not found. */
-    fun dismissMediaData(key: String, delayMs: Long): Boolean {
+    fun dismissMediaData(key: String, delayMs: Long, userInitiated: Boolean): Boolean {
         val existed = mediaDataRepository.mediaEntries.value[key] != null
         backgroundExecutor.execute {
             mediaDataRepository.mediaEntries.value[key]?.let { mediaData ->
@@ -602,16 +602,19 @@
                 }
             }
         }
-        foregroundExecutor.executeDelayed({ removeEntry(key) }, delayMs)
+        foregroundExecutor.executeDelayed(
+            { removeEntry(key, userInitiated = userInitiated) },
+            delayMs
+        )
         return existed
     }
 
     /** Dismiss a media entry. Returns false if the corresponding key was not found. */
-    fun dismissMediaData(instanceId: InstanceId, delayMs: Long): Boolean {
+    fun dismissMediaData(instanceId: InstanceId, delayMs: Long, userInitiated: Boolean): Boolean {
         val mediaEntries = mediaDataRepository.mediaEntries.value
         val filteredEntries = mediaEntries.filter { (_, data) -> data.instanceId == instanceId }
         return if (filteredEntries.isNotEmpty()) {
-            dismissMediaData(filteredEntries.keys.first(), delayMs)
+            dismissMediaData(filteredEntries.keys.first(), delayMs, userInitiated)
         } else {
             false
         }
@@ -1579,7 +1582,7 @@
         ) {}
 
         /** Called whenever a previously existing Media notification was removed. */
-        fun onMediaDataRemoved(key: String) {}
+        fun onMediaDataRemoved(key: String, userInitiated: Boolean) {}
 
         /**
          * Called whenever a previously existing Smartspace media data was removed.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index 0e2814b..043fbfa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -111,10 +111,10 @@
         }
     }
 
-    override fun onMediaDataRemoved(key: String) {
+    override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
         val token = entries.remove(key)
         token?.stop()
-        token?.let { listeners.forEach { it.onKeyRemoved(key) } }
+        token?.let { listeners.forEach { it.onKeyRemoved(key, userInitiated) } }
     }
 
     fun dump(pw: PrintWriter) {
@@ -136,7 +136,7 @@
         /** Called when the route has changed for a given notification. */
         fun onMediaDeviceChanged(key: String, oldKey: String?, data: MediaDeviceData?)
         /** Called when the notification was removed. */
-        fun onKeyRemoved(key: String)
+        fun onKeyRemoved(key: String, userInitiated: Boolean)
     }
 
     private inner class Entry(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
index b2a8f2e..b178d84 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
@@ -137,7 +137,7 @@
                 // farther and dismiss the media data so that media controls for the local session
                 // don't hang around while casting.
                 if (!keyedTokens.get(key)!!.contains(TokenId(remote.sessionToken))) {
-                    dispatchMediaDataRemoved(key)
+                    dispatchMediaDataRemoved(key, userInitiated = false)
                 }
             }
         }
@@ -151,11 +151,11 @@
         backgroundExecutor.execute { dispatchSmartspaceMediaDataLoaded(key, data) }
     }
 
-    override fun onMediaDataRemoved(key: String) {
+    override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
         // Queue on background thread to ensure ordering of loaded and removed events is maintained.
         backgroundExecutor.execute {
             keyedTokens.remove(key)
-            dispatchMediaDataRemoved(key)
+            dispatchMediaDataRemoved(key, userInitiated)
         }
     }
 
@@ -174,8 +174,10 @@
         }
     }
 
-    private fun dispatchMediaDataRemoved(key: String) {
-        foregroundExecutor.execute { listeners.toSet().forEach { it.onMediaDataRemoved(key) } }
+    private fun dispatchMediaDataRemoved(key: String, userInitiated: Boolean) {
+        foregroundExecutor.execute {
+            listeners.toSet().forEach { it.onMediaDataRemoved(key, userInitiated) }
+        }
     }
 
     private fun dispatchSmartspaceMediaDataLoaded(key: String, info: SmartspaceMediaData) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
index 29f3967..fc31903 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
@@ -169,7 +169,7 @@
         mediaListeners[key] = PlaybackStateListener(key, data)
     }
 
-    override fun onMediaDataRemoved(key: String) {
+    override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
         mediaListeners.remove(key)?.destroy()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index c888935..9e62300 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -205,12 +205,12 @@
         )
     }
 
-    override fun dismissMediaData(key: String, delay: Long): Boolean {
-        return mediaDataProcessor.dismissMediaData(key, delay)
+    override fun dismissMediaData(key: String, delay: Long, userInitiated: Boolean): Boolean {
+        return mediaDataProcessor.dismissMediaData(key, delay, userInitiated)
     }
 
     fun removeMediaControl(instanceId: InstanceId, delay: Long) {
-        mediaDataProcessor.dismissMediaData(instanceId, delay)
+        mediaDataProcessor.dismissMediaData(instanceId, delay, userInitiated = false)
     }
 
     override fun dismissSmartspaceRecommendation(key: String, delay: Long) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
index 9f2d132..d1fee90 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
@@ -90,7 +90,8 @@
         instanceId: InstanceId,
         delayMs: Long
     ): Boolean {
-        val dismissed = mediaDataProcessor.dismissMediaData(instanceId, delayMs)
+        val dismissed =
+            mediaDataProcessor.dismissMediaData(instanceId, delayMs, userInitiated = true)
         if (!dismissed) {
             Log.w(
                 TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 45b68ca..b072534 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -193,6 +193,7 @@
     private val mediaContent: ViewGroup
     @VisibleForTesting var pageIndicator: PageIndicator
     private var needsReordering: Boolean = false
+    private var isUserInitiatedRemovalQueued: Boolean = false
     private var keysNeedRemoval = mutableSetOf<String>()
     var shouldScrollToKey: Boolean = false
     private var isRtl: Boolean = false
@@ -385,12 +386,15 @@
                 reorderAllPlayers(previousVisiblePlayerKey = null)
             }
 
-            keysNeedRemoval.forEach { removePlayer(it) }
+            keysNeedRemoval.forEach {
+                removePlayer(it, userInitiated = isUserInitiatedRemovalQueued)
+            }
             if (keysNeedRemoval.size > 0) {
                 // Carousel visibility may need to be updated after late removals
                 updateHostVisibility()
             }
             keysNeedRemoval.clear()
+            isUserInitiatedRemovalQueued = false
 
             // Update user visibility so that no extra impression will be logged when
             // activeMediaIndex resets to 0
@@ -474,18 +478,18 @@
 
                     val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
                     if (canRemove && !Utils.useMediaResumption(context)) {
-                        // This view isn't playing, let's remove this! This happens e.g. when
-                        // dismissing/timing out a view. We still have the data around because
-                        // resumption could be on, but we should save the resources and release
-                        // this.
+                        // This media control is both paused and timed out, and the resumption
+                        // setting is off - let's remove it
                         if (isReorderingAllowed) {
-                            onMediaDataRemoved(key)
+                            onMediaDataRemoved(key, userInitiated = MediaPlayerData.isSwipedAway)
                         } else {
+                            isUserInitiatedRemovalQueued = MediaPlayerData.isSwipedAway
                             keysNeedRemoval.add(key)
                         }
                     } else {
                         keysNeedRemoval.remove(key)
                     }
+                    MediaPlayerData.isSwipedAway = false
                 }
 
                 override fun onSmartspaceMediaDataLoaded(
@@ -565,11 +569,12 @@
                             addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
                         }
                     }
+                    MediaPlayerData.isSwipedAway = false
                 }
 
-                override fun onMediaDataRemoved(key: String) {
-                    debugLogger.logMediaRemoved(key)
-                    removePlayer(key)
+                override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
+                    debugLogger.logMediaRemoved(key, userInitiated)
+                    removePlayer(key, userInitiated = userInitiated)
                 }
 
                 override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
@@ -1033,7 +1038,8 @@
     fun removePlayer(
         key: String,
         dismissMediaData: Boolean = true,
-        dismissRecommendation: Boolean = true
+        dismissRecommendation: Boolean = true,
+        userInitiated: Boolean = false,
     ): MediaControlPanel? {
         if (key == MediaPlayerData.smartspaceMediaKey()) {
             MediaPlayerData.smartspaceMediaData?.let {
@@ -1052,7 +1058,7 @@
 
             if (dismissMediaData) {
                 // Inform the media manager of a potentially late dismissal
-                mediaManager.dismissMediaData(key, delay = 0L)
+                mediaManager.dismissMediaData(key, delay = 0L, userInitiated = userInitiated)
             }
             if (dismissRecommendation) {
                 // Inform the media manager of a potentially late dismissal
@@ -1512,7 +1518,8 @@
         }
     }
 
-    private fun onSwipeToDismiss() {
+    @VisibleForTesting
+    fun onSwipeToDismiss() {
         if (mediaFlags.isMediaControlsRefactorEnabled()) {
             mediaCarouselViewModel.onSwipeToDismiss()
             return
@@ -1531,6 +1538,7 @@
                 it.mIsImpressed = false
             }
         }
+        MediaPlayerData.isSwipedAway = true
         logger.logSwipeDismiss()
         mediaManager.onSwipeToDismiss()
     }
@@ -1557,6 +1565,7 @@
                 "state: ${desiredHostState?.expansion}, " +
                     "only active ${desiredHostState?.showsOnlyActiveMedia}"
             )
+            println("isSwipedAway: ${MediaPlayerData.isSwipedAway}")
         }
     }
 }
@@ -1595,7 +1604,7 @@
         val data: MediaData,
         val key: String,
         val updateTime: Long = 0,
-        val isSsReactivated: Boolean = false
+        val isSsReactivated: Boolean = false,
     )
 
     private val comparator =
@@ -1620,6 +1629,9 @@
     // A map that tracks order of visible media players before they get reordered.
     private val visibleMediaPlayers = LinkedHashMap<String, MediaSortKey>()
 
+    // Whether the user swiped away the carousel since its last update
+    internal var isSwipedAway: Boolean = false
+
     fun addMediaPlayer(
         key: String,
         data: MediaData,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
index ebf1c6a..1be25a7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
@@ -53,8 +53,16 @@
             { "add player $str1, active: $bool1" }
         )
 
-    fun logMediaRemoved(key: String) =
-        buffer.log(TAG, LogLevel.DEBUG, { str1 = key }, { "removing player $str1" })
+    fun logMediaRemoved(key: String, userInitiated: Boolean) =
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = key
+                bool1 = userInitiated
+            },
+            { "removing player $str1, by user $bool1" }
+        )
 
     fun logRecommendationLoaded(key: String, isActive: Boolean) =
         buffer.log(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index e6c785e..0bc3c439 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -786,10 +786,11 @@
             if (mKey != null) {
                 closeGuts();
                 if (!mMediaDataManagerLazy.get().dismissMediaData(mKey,
-                        MediaViewController.GUTS_ANIMATION_DURATION + 100)) {
+                        /* delay */ MediaViewController.GUTS_ANIMATION_DURATION + 100,
+                        /* userInitiated */ true)) {
                     Log.w(TAG, "Manager failed to dismiss media " + mKey);
                     // Remove directly from carousel so user isn't stuck with defunct controls
-                    mMediaCarouselController.removePlayer(mKey, false, false);
+                    mMediaCarouselController.removePlayer(mKey, false, false, true);
                 }
             } else {
                 Log.w(TAG, "Dismiss media with null notification. Token uid="
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index eca76b6..91050c8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -105,7 +105,7 @@
                 updateViewVisibility()
             }
 
-            override fun onMediaDataRemoved(key: String) {
+            override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
                 updateViewVisibility()
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
index 88a5f78..061e7ec 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
@@ -48,7 +48,7 @@
         }
 
         @Override
-        public void onMediaDataRemoved(@NonNull String key) {
+        public void onMediaDataRemoved(@NonNull String key, boolean userInitiated) {
             final boolean hasActiveMedia = mMediaDataManager.hasActiveMedia();
             if (DEBUG) {
                 Log.d(TAG, "onMediaDataRemoved(" + key + "), mAdded=" + mAdded + ", hasActiveMedia="
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
index 5c17fd1..3bda775 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
@@ -20,9 +20,9 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
 
 @Composable
@@ -30,8 +30,8 @@
     viewModel: EditModeViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val gridLayout by viewModel.gridLayout.collectAsState()
-    val tiles by viewModel.tiles.collectAsState(emptyList())
+    val gridLayout by viewModel.gridLayout.collectAsStateWithLifecycle()
+    val tiles by viewModel.tiles.collectAsStateWithLifecycle(emptyList())
 
     BackHandler { viewModel.stopEditing() }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
index dc43091..bac0f60 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
@@ -53,7 +53,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -72,6 +71,7 @@
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.stateDescription
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.Expandable
 import com.android.compose.theme.colorAttr
 import com.android.systemui.common.shared.model.Icon
@@ -116,8 +116,8 @@
             tiles.forEach { it.startListening(token) }
             onDispose { tiles.forEach { it.stopListening(token) } }
         }
-        val iconTilesSpecs by iconTilesInteractor.iconTilesSpecs.collectAsState()
-        val columns by gridSizeInteractor.columns.collectAsState()
+        val iconTilesSpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
+        val columns by gridSizeInteractor.columns.collectAsStateWithLifecycle()
 
         TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
             items(
@@ -150,7 +150,7 @@
         val state: TileUiState by
             tile.state
                 .mapLatest { it.toUiState() }
-                .collectAsState(initial = tile.currentState.toUiState())
+                .collectAsStateWithLifecycle(initialValue = tile.currentState.toUiState())
         val context = LocalContext.current
 
         Expandable(
@@ -201,10 +201,13 @@
         val addTileToEnd: (TileSpec) -> Unit by rememberUpdatedState {
             onAddTile(it, POSITION_AT_END)
         }
-        val iconOnlySpecs by iconTilesInteractor.iconTilesSpecs.collectAsState(initial = emptySet())
+        val iconOnlySpecs by
+            iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle(
+                initialValue = emptySet()
+            )
         val isIconOnly: (TileSpec) -> Boolean =
             remember(iconOnlySpecs) { { tileSpec: TileSpec -> tileSpec in iconOnlySpecs } }
-        val columns by gridSizeInteractor.columns.collectAsState()
+        val columns by gridSizeInteractor.columns.collectAsStateWithLifecycle()
 
         TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
             // These Text are just placeholders to see the different sections. Not final UI.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
index 2f32d72..2dab7c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
@@ -17,15 +17,15 @@
 package com.android.systemui.qs.panels.ui.compose
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
 
 @Composable
 fun TileGrid(viewModel: TileGridViewModel, modifier: Modifier = Modifier) {
-    val gridLayout by viewModel.gridLayout.collectAsState()
-    val tiles by viewModel.tileViewModels.collectAsState(emptyList())
+    val gridLayout by viewModel.gridLayout.collectAsStateWithLifecycle()
+    val tiles by viewModel.tileViewModels.collectAsStateWithLifecycle(emptyList())
 
     gridLayout.TileGrid(tiles, modifier)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
index 4e290e6..6694878 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
@@ -20,7 +20,6 @@
 import android.app.NotificationManager
 import android.content.Context
 import android.content.Intent
-import android.content.pm.LauncherApps
 import android.content.res.Resources
 import android.net.Uri
 import android.os.Handler
@@ -63,7 +62,6 @@
     private val panelInteractor: PanelInteractor,
     private val issueRecordingState: IssueRecordingState,
     private val iActivityManager: IActivityManager,
-    private val launcherApps: LauncherApps,
 ) :
     RecordingService(
         controller,
@@ -85,7 +83,7 @@
         when (intent?.action) {
             ACTION_START -> {
                 TraceUtils.traceStart(
-                    contentResolver,
+                    this,
                     DEFAULT_TRACE_TAGS,
                     DEFAULT_BUFFER_SIZE,
                     DEFAULT_IS_INCLUDING_WINSCOPE,
@@ -104,11 +102,7 @@
             }
             ACTION_STOP,
             ACTION_STOP_NOTIF -> {
-                // ViewCapture needs to save it's data before it is disabled, or else the data will
-                // be lost. This is expected to change in the near future, and when that happens
-                // this line should be removed.
-                launcherApps.saveViewCaptureData()
-                TraceUtils.traceStop(contentResolver)
+                TraceUtils.traceStop(this)
                 issueRecordingState.isRecording = false
             }
             ACTION_SHARE -> {
@@ -142,7 +136,7 @@
 
     private fun shareRecording(screenRecording: Uri?) {
         val traces =
-            TraceUtils.traceDump(contentResolver, TRACE_FILE_NAME).getOrElse {
+            TraceUtils.traceDump(this, TRACE_FILE_NAME).getOrElse {
                 Log.v(
                     TAG,
                     "Traces were not present. This can happen if users double" +
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 4eca51d..4ab0918 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -33,10 +33,11 @@
 import android.view.WindowManagerGlobal
 import com.android.app.tracing.coroutines.launch
 import com.android.internal.infra.ServiceConnector
-import com.android.systemui.Flags.screenshotActionDismissSystemWindows
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.screenshot.proxy.SystemUiProxy
 import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.shared.system.ActivityManagerWrapper
 import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -54,8 +55,8 @@
     private val activityManagerWrapper: ActivityManagerWrapper,
     @Application private val applicationScope: CoroutineScope,
     @Main private val mainDispatcher: CoroutineDispatcher,
+    private val systemUiProxy: SystemUiProxy,
     private val displayTracker: DisplayTracker,
-    private val keyguardController: ScreenshotKeyguardController,
 ) {
     /**
      * Execute the given intent with startActivity while performing operations for screenshot action
@@ -83,14 +84,12 @@
         options: ActivityOptions?,
         transitionCoordinator: ExitTransitionCoordinator?,
     ) {
-        if (screenshotActionDismissSystemWindows()) {
-            keyguardController.dismiss()
+        if (Flags.fixScreenshotActionDismissSystemWindows()) {
             activityManagerWrapper.closeSystemWindows(
                 CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT
             )
-        } else {
-            dismissKeyguard()
         }
+        systemUiProxy.dismissKeyguard()
         transitionCoordinator?.startExit()
 
         if (user == myUserHandle()) {
@@ -110,27 +109,6 @@
         }
     }
 
-    private val proxyConnector: ServiceConnector<IScreenshotProxy> =
-        ServiceConnector.Impl(
-            context,
-            Intent(context, ScreenshotProxyService::class.java),
-            Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
-            context.userId,
-            IScreenshotProxy.Stub::asInterface,
-        )
-
-    private suspend fun dismissKeyguard() {
-        val completion = CompletableDeferred<Unit>()
-        val onDoneBinder =
-            object : IOnDoneCallback.Stub() {
-                override fun onDone(success: Boolean) {
-                    completion.complete(Unit)
-                }
-            }
-        proxyConnector.post { it.dismissKeyguard(onDoneBinder) }
-        completion.await()
-    }
-
     private fun getCrossProfileConnector(user: UserHandle): ServiceConnector<ICrossProfileService> =
         ServiceConnector.Impl<ICrossProfileService>(
             context,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt
deleted file mode 100644
index 7696bbe..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2023 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.screenshot
-
-import android.content.Context
-import android.content.Intent
-import com.android.internal.infra.ServiceConnector
-import javax.inject.Inject
-import kotlinx.coroutines.CompletableDeferred
-
-open class ScreenshotKeyguardController @Inject constructor(context: Context) {
-    private val proxyConnector: ServiceConnector<IScreenshotProxy> =
-        ServiceConnector.Impl(
-            context,
-            Intent(context, ScreenshotProxyService::class.java),
-            Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
-            context.userId,
-            IScreenshotProxy.Stub::asInterface
-        )
-
-    suspend fun dismiss() {
-        val completion = CompletableDeferred<Unit>()
-        val onDoneBinder =
-            object : IOnDoneCallback.Stub() {
-                override fun onDone(success: Boolean) {
-                    completion.complete(Unit)
-                }
-            }
-        proxyConnector.post { it.dismissKeyguard(onDoneBinder) }
-        completion.await()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
index 1e66cd1..846884f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
@@ -31,6 +31,7 @@
 import android.view.WindowManager
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
+import androidx.appcompat.content.res.AppCompatResources
 import androidx.core.animation.doOnEnd
 import androidx.core.animation.doOnStart
 import com.android.internal.logging.UiEventLogger
@@ -58,6 +59,7 @@
     private val logger: UiEventLogger,
     private val viewModel: ScreenshotViewModel,
     private val windowManager: WindowManager,
+    shelfViewBinder: ScreenshotShelfViewBinder,
     private val thumbnailObserver: ThumbnailObserver,
     @Assisted private val context: Context,
     @Assisted private val displayId: Int
@@ -69,7 +71,17 @@
     override var callbacks: ScreenshotView.ScreenshotViewCallback? = null
     override var screenshot: ScreenshotData? = null
         set(value) {
-            viewModel.setScreenshotBitmap(value?.bitmap)
+            value?.let {
+                viewModel.setScreenshotBitmap(it.bitmap)
+                val badgeBg =
+                    AppCompatResources.getDrawable(context, R.drawable.overlay_badge_background)
+                val user = it.userHandle
+                if (badgeBg != null && user != null) {
+                    viewModel.setScreenshotBadge(
+                        context.packageManager.getUserBadgedIcon(badgeBg, user)
+                    )
+                }
+            }
             field = value
         }
 
@@ -81,7 +93,7 @@
     private val animationController = ScreenshotAnimationController(view)
 
     init {
-        ScreenshotShelfViewBinder.bind(
+        shelfViewBinder.bind(
             view,
             viewModel,
             LayoutInflater.from(context),
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt
index 2243ade..2a98068 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt
@@ -23,8 +23,9 @@
 import com.android.systemui.res.R
 import com.android.systemui.screenshot.ui.TransitioningIconDrawable
 import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel
+import javax.inject.Inject
 
-object ActionButtonViewBinder {
+class ActionButtonViewBinder @Inject constructor() {
     /** Binds the given view to the given view-model */
     fun bind(view: View, viewModel: ActionButtonViewModel) {
         val iconView = view.requireViewById<ImageView>(R.id.overlay_action_chip_icon)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
index 89f904a..c7bc50cb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
@@ -35,10 +35,13 @@
 import com.android.systemui.screenshot.ui.viewmodel.AnimationState
 import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
 import com.android.systemui.util.children
+import javax.inject.Inject
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 
-object ScreenshotShelfViewBinder {
+class ScreenshotShelfViewBinder
+@Inject
+constructor(private val buttonViewBinder: ActionButtonViewBinder) {
     fun bind(
         view: ScreenshotShelfView,
         viewModel: ScreenshotViewModel,
@@ -68,6 +71,7 @@
         dismissButton.setOnClickListener {
             onDismissalRequested(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL, null)
         }
+        val badgeView = view.requireViewById<ImageView>(R.id.screenshot_badge)
 
         // use immediate dispatcher to ensure screenshot bitmap is set before animation
         view.repeatWhenAttached(Dispatchers.Main.immediate) {
@@ -87,6 +91,12 @@
                         }
                     }
                     launch {
+                        viewModel.badge.collect { badge ->
+                            badgeView.setImageDrawable(badge)
+                            badgeView.visibility = if (badge != null) View.VISIBLE else View.GONE
+                        }
+                    }
+                    launch {
                         viewModel.previewAction.collect { onClick ->
                             previewView.setOnClickListener { onClick?.invoke() }
                         }
@@ -151,14 +161,14 @@
             val currentView: View? = actionsContainer.getChildAt(index)
             if (action.id == currentView?.tag) {
                 // Same ID, update the display
-                ActionButtonViewBinder.bind(currentView, action)
+                buttonViewBinder.bind(currentView, action)
             } else {
                 // Different ID. Removals have already happened so this must
                 // mean that the new action must be inserted here.
                 val actionButton =
                     layoutInflater.inflate(R.layout.shelf_action_chip, actionsContainer, false)
                 actionsContainer.addView(actionButton, index)
-                ActionButtonViewBinder.bind(actionButton, action)
+                buttonViewBinder.bind(actionButton, action)
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
index 5f36f73..81bc281 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.screenshot.ui.viewmodel
 
 import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
 import android.util.Log
 import android.view.accessibility.AccessibilityManager
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -25,6 +26,8 @@
 class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager) {
     private val _preview = MutableStateFlow<Bitmap?>(null)
     val preview: StateFlow<Bitmap?> = _preview
+    private val _badge = MutableStateFlow<Drawable?>(null)
+    val badge: StateFlow<Drawable?> = _badge
     private val _previewAction = MutableStateFlow<(() -> Unit)?>(null)
     val previewAction: StateFlow<(() -> Unit)?> = _previewAction
     private val _actions = MutableStateFlow(emptyList<ActionButtonViewModel>())
@@ -39,6 +42,10 @@
         _preview.value = bitmap
     }
 
+    fun setScreenshotBadge(badge: Drawable?) {
+        _badge.value = badge
+    }
+
     fun setPreviewAction(onClick: () -> Unit) {
         _previewAction.value = onClick
     }
@@ -109,6 +116,7 @@
 
     fun reset() {
         _preview.value = null
+        _badge.value = null
         _previewAction.value = null
         _actions.value = listOf()
         _animationState.value = AnimationState.NOT_STARTED
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index d7d3732..5bf2f41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.Flags.mediaControlsUserInitiatedDismiss;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
@@ -175,14 +177,18 @@
             }
 
             @Override
-            public void onMediaDataRemoved(@NonNull String key) {
+            public void onMediaDataRemoved(@NonNull String key, boolean userInitiated) {
+                if (mediaControlsUserInitiatedDismiss() && !userInitiated) {
+                    // Dismissing the notification will send the app's deleteIntent, so ignore if
+                    // this was an automatic removal
+                    Log.d(TAG, "Not dismissing " + key + " because it was removed by the system");
+                    return;
+                }
                 mNotifPipeline.getAllNotifs()
                         .stream()
                         .filter(entry -> Objects.equals(entry.getKey(), key))
                         .findAny()
                         .ifPresent(entry -> {
-                            // TODO(b/160713608): "removing" this notification won't happen and
-                            //  won't send the 'deleteIntent' if the notification is ongoing.
                             mNotifCollection.dismissNotification(entry,
                                     getDismissedByUserStats(entry));
                         });
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 933eb20..42bf4e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -31,15 +31,20 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.expansionChanges
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
 import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
 import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.headsUpEvents
 import com.android.systemui.util.asIndenting
@@ -106,6 +111,10 @@
     }
 
     private fun attachUnseenFilter(pipeline: NotifPipeline) {
+        if (NotificationMinimalismPrototype.V2.isEnabled) {
+            pipeline.addPromoter(unseenNotifPromoter)
+            pipeline.addOnBeforeTransformGroupsListener(::pickOutTopUnseenNotif)
+        }
         pipeline.addFinalizeFilter(unseenNotifFilter)
         pipeline.addCollectionListener(collectionListener)
         scope.launch { trackUnseenFilterSettingChanges() }
@@ -263,7 +272,10 @@
     }
 
     private fun unseenFeatureEnabled(): Flow<Boolean> {
-        if (NotificationMinimalismPrototype.V1.isEnabled) {
+        if (
+            NotificationMinimalismPrototype.V1.isEnabled ||
+                NotificationMinimalismPrototype.V2.isEnabled
+        ) {
             return flowOf(true)
         }
         return secureSettings
@@ -334,6 +346,57 @@
             }
         }
 
+    private fun pickOutTopUnseenNotif(list: List<ListEntry>) {
+        if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return
+        // Only ever elevate a top unseen notification on keyguard, not even locked shade
+        if (statusBarStateController.state != StatusBarState.KEYGUARD) {
+            seenNotificationsInteractor.setTopUnseenNotification(null)
+            return
+        }
+        // On keyguard pick the top-ranked unseen or ongoing notification to elevate
+        seenNotificationsInteractor.setTopUnseenNotification(
+            list
+                .asSequence()
+                .flatMap {
+                    when (it) {
+                        is NotificationEntry -> listOfNotNull(it)
+                        is GroupEntry -> it.children
+                        else -> error("unhandled type of $it")
+                    }
+                }
+                .filter { shouldIgnoreUnseenCheck(it) || it in unseenNotifications }
+                .minByOrNull { it.ranking.rank }
+        )
+    }
+
+    @VisibleForTesting
+    internal val unseenNotifPromoter =
+        object : NotifPromoter("$TAG-unseen") {
+            override fun shouldPromoteToTopLevel(child: NotificationEntry): Boolean =
+                if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) false
+                else
+                    seenNotificationsInteractor.isTopUnseenNotification(child) &&
+                        NotificationMinimalismPrototype.V2.ungroupTopUnseen
+        }
+
+    val unseenNotifSectioner =
+        object : NotifSectioner("Unseen", BUCKET_FOREGROUND_SERVICE) {
+            override fun isInSection(entry: ListEntry): Boolean {
+                if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return false
+                if (
+                    seenNotificationsInteractor.isTopUnseenNotification(entry.representativeEntry)
+                ) {
+                    return true
+                }
+                if (entry !is GroupEntry) {
+                    return false
+                }
+                return entry.children.any {
+                    seenNotificationsInteractor.isTopUnseenNotification(it)
+                }
+            }
+        }
+
     @VisibleForTesting
     internal val unseenNotifFilter =
         object : NotifFilter("$TAG-unseen") {
@@ -351,7 +414,9 @@
              * allow seen notifications to appear in the locked shade.
              */
             private fun isOnKeyguard(): Boolean =
-                if (
+                if (NotificationMinimalismPrototype.V2.isEnabled) {
+                    false // disable this feature under this prototype
+                } else if (
                     NotificationMinimalismPrototype.V1.isEnabled &&
                         NotificationMinimalismPrototype.V1.showOnLockedShade
                 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 36c12a7..387444b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -24,47 +24,50 @@
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import javax.inject.Inject
 
 /**
- * Handles the attachment of [Coordinator]s to the [NotifPipeline] so that the
- * Coordinators can register their respective callbacks.
+ * Handles the attachment of [Coordinator]s to the [NotifPipeline] so that the Coordinators can
+ * register their respective callbacks.
  */
 interface NotifCoordinators : Coordinator, PipelineDumpable
 
 @CoordinatorScope
-class NotifCoordinatorsImpl @Inject constructor(
-        sectionStyleProvider: SectionStyleProvider,
-        featureFlags: FeatureFlags,
-        dataStoreCoordinator: DataStoreCoordinator,
-        hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
-        hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
-        keyguardCoordinator: KeyguardCoordinator,
-        rankingCoordinator: RankingCoordinator,
-        colorizedFgsCoordinator: ColorizedFgsCoordinator,
-        deviceProvisionedCoordinator: DeviceProvisionedCoordinator,
-        bubbleCoordinator: BubbleCoordinator,
-        headsUpCoordinator: HeadsUpCoordinator,
-        gutsCoordinator: GutsCoordinator,
-        conversationCoordinator: ConversationCoordinator,
-        debugModeCoordinator: DebugModeCoordinator,
-        groupCountCoordinator: GroupCountCoordinator,
-        groupWhenCoordinator: GroupWhenCoordinator,
-        mediaCoordinator: MediaCoordinator,
-        preparationCoordinator: PreparationCoordinator,
-        remoteInputCoordinator: RemoteInputCoordinator,
-        rowAlertTimeCoordinator: RowAlertTimeCoordinator,
-        rowAppearanceCoordinator: RowAppearanceCoordinator,
-        stackCoordinator: StackCoordinator,
-        shadeEventCoordinator: ShadeEventCoordinator,
-        smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
-        viewConfigCoordinator: ViewConfigCoordinator,
-        visualStabilityCoordinator: VisualStabilityCoordinator,
-        sensitiveContentCoordinator: SensitiveContentCoordinator,
-        dismissibilityCoordinator: DismissibilityCoordinator,
-        dreamCoordinator: DreamCoordinator,
-        statsLoggerCoordinator: NotificationStatsLoggerCoordinator,
+class NotifCoordinatorsImpl
+@Inject
+constructor(
+    sectionStyleProvider: SectionStyleProvider,
+    featureFlags: FeatureFlags,
+    dataStoreCoordinator: DataStoreCoordinator,
+    hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
+    hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
+    keyguardCoordinator: KeyguardCoordinator,
+    rankingCoordinator: RankingCoordinator,
+    colorizedFgsCoordinator: ColorizedFgsCoordinator,
+    deviceProvisionedCoordinator: DeviceProvisionedCoordinator,
+    bubbleCoordinator: BubbleCoordinator,
+    headsUpCoordinator: HeadsUpCoordinator,
+    gutsCoordinator: GutsCoordinator,
+    conversationCoordinator: ConversationCoordinator,
+    debugModeCoordinator: DebugModeCoordinator,
+    groupCountCoordinator: GroupCountCoordinator,
+    groupWhenCoordinator: GroupWhenCoordinator,
+    mediaCoordinator: MediaCoordinator,
+    preparationCoordinator: PreparationCoordinator,
+    remoteInputCoordinator: RemoteInputCoordinator,
+    rowAlertTimeCoordinator: RowAlertTimeCoordinator,
+    rowAppearanceCoordinator: RowAppearanceCoordinator,
+    stackCoordinator: StackCoordinator,
+    shadeEventCoordinator: ShadeEventCoordinator,
+    smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
+    viewConfigCoordinator: ViewConfigCoordinator,
+    visualStabilityCoordinator: VisualStabilityCoordinator,
+    sensitiveContentCoordinator: SensitiveContentCoordinator,
+    dismissibilityCoordinator: DismissibilityCoordinator,
+    dreamCoordinator: DreamCoordinator,
+    statsLoggerCoordinator: NotificationStatsLoggerCoordinator,
 ) : NotifCoordinators {
 
     private val mCoreCoordinators: MutableList<CoreCoordinator> = ArrayList()
@@ -114,6 +117,9 @@
         // Manually add Ordered Sections
         mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp
         mOrderedSections.add(colorizedFgsCoordinator.sectioner) // ForegroundService
+        if (NotificationMinimalismPrototype.V2.isEnabled) {
+            mOrderedSections.add(keyguardCoordinator.unseenNotifSectioner) // Unseen (FGS)
+        }
         mOrderedSections.add(conversationCoordinator.peopleAlertingSectioner) // People Alerting
         if (!SortBySectionTimeFlag.isEnabled) {
             mOrderedSections.add(conversationCoordinator.peopleSilentSectioner) // People Silent
@@ -124,22 +130,26 @@
 
         sectionStyleProvider.setMinimizedSections(setOf(rankingCoordinator.minimizedSectioner))
         if (SortBySectionTimeFlag.isEnabled) {
-            sectionStyleProvider.setSilentSections(listOf(
+            sectionStyleProvider.setSilentSections(
+                listOf(
                     rankingCoordinator.silentSectioner,
                     rankingCoordinator.minimizedSectioner,
-            ))
+                )
+            )
         } else {
-            sectionStyleProvider.setSilentSections(listOf(
+            sectionStyleProvider.setSilentSections(
+                listOf(
                     conversationCoordinator.peopleSilentSectioner,
                     rankingCoordinator.silentSectioner,
                     rankingCoordinator.minimizedSectioner,
-            ))
+                )
+            )
         }
     }
 
     /**
-     * Sends the pipeline to each coordinator when the pipeline is ready to accept
-     * [Pluggable]s, [NotifCollectionListener]s and [NotifLifetimeExtender]s.
+     * Sends the pipeline to each coordinator when the pipeline is ready to accept [Pluggable]s,
+     * [NotifCollectionListener]s and [NotifLifetimeExtender]s.
      */
     override fun attach(pipeline: NotifPipeline) {
         for (c in mCoreCoordinators) {
@@ -155,10 +165,11 @@
      * As part of the NotifPipeline dumpable, dumps the list of coordinators; sections are omitted
      * as they are dumped in the RenderStageManager instead.
      */
-    override fun dumpPipeline(d: PipelineDumper) = with(d) {
-        dump("core coordinators", mCoreCoordinators)
-        dump("normal coordinators", mCoordinators)
-    }
+    override fun dumpPipeline(d: PipelineDumper) =
+        with(d) {
+            dump("core coordinators", mCoreCoordinators)
+            dump("normal coordinators", mCoordinators)
+        }
 
     companion object {
         private const val TAG = "NotifCoordinators"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 350e88e..caa6c17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -38,6 +38,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
+import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.Compile;
 import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -63,6 +65,7 @@
     public static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
     private final DelayableExecutor mDelayableExecutor;
     private final HeadsUpManager mHeadsUpManager;
+    private final SeenNotificationsInteractor mSeenNotificationsInteractor;
     private final ShadeAnimationInteractor mShadeAnimationInteractor;
     private final StatusBarStateController mStatusBarStateController;
     private final JavaAdapter mJavaAdapter;
@@ -101,6 +104,7 @@
             HeadsUpManager headsUpManager,
             ShadeAnimationInteractor shadeAnimationInteractor,
             JavaAdapter javaAdapter,
+            SeenNotificationsInteractor seenNotificationsInteractor,
             StatusBarStateController statusBarStateController,
             VisibilityLocationProvider visibilityLocationProvider,
             VisualStabilityProvider visualStabilityProvider,
@@ -109,6 +113,7 @@
         mHeadsUpManager = headsUpManager;
         mShadeAnimationInteractor = shadeAnimationInteractor;
         mJavaAdapter = javaAdapter;
+        mSeenNotificationsInteractor = seenNotificationsInteractor;
         mVisibilityLocationProvider = visibilityLocationProvider;
         mVisualStabilityProvider = visualStabilityProvider;
         mWakefulnessLifecycle = wakefulnessLifecycle;
@@ -142,8 +147,15 @@
     private final NotifStabilityManager mNotifStabilityManager =
             new NotifStabilityManager("VisualStabilityCoordinator") {
                 private boolean canMoveForHeadsUp(NotificationEntry entry) {
-                    return entry != null && mHeadsUpManager.isHeadsUpEntry(entry.getKey())
-                            && !mVisibilityLocationProvider.isInVisibleLocation(entry);
+                    if (entry == null) {
+                        return false;
+                    }
+                    boolean isTopUnseen = NotificationMinimalismPrototype.V2.isEnabled()
+                            && mSeenNotificationsInteractor.isTopUnseenNotification(entry);
+                    if (isTopUnseen || mHeadsUpManager.isHeadsUpEntry(entry.getKey())) {
+                        return !mVisibilityLocationProvider.isInVisibleLocation(entry);
+                    }
+                    return false;
                 }
 
                 @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
index 5c844bc..e2c9e02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
@@ -41,6 +41,9 @@
 
     /** Stats about the list of notifications attached to the shade */
     val notifStats = MutableStateFlow(NotifStats.empty)
+
+    /** The key of the top unseen notification */
+    val topUnseenNotificationKey = MutableStateFlow<String?>(null)
 }
 
 /** Represents the notification list, comprised of groups and individual notifications. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
index 1f7ab96..42828d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
@@ -17,7 +17,9 @@
 package com.android.systemui.statusbar.notification.domain.interactor
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
 import javax.inject.Inject
 import kotlinx.coroutines.flow.StateFlow
 
@@ -36,4 +38,15 @@
     fun setHasFilteredOutSeenNotifications(value: Boolean) {
         notificationListRepository.hasFilteredOutSeenNotifications.value = value
     }
+
+    /** Set the entry that is identified as the top unseen notification. */
+    fun setTopUnseenNotification(entry: NotificationEntry?) {
+        if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return
+        notificationListRepository.topUnseenNotificationKey.value = entry?.key
+    }
+
+    /** Determine if the given notification is the top unseen notification. */
+    fun isTopUnseenNotification(entry: NotificationEntry?): Boolean =
+        if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) false
+        else entry != null && notificationListRepository.topUnseenNotificationKey.value == entry.key
 }
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 747cb3c..4f1056c 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
@@ -590,7 +590,9 @@
             mMenuRow.setAppName(mAppName);
         }
         if (mIsSummaryWithChildren) {
-            if (!AsyncGroupHeaderViewInflation.isEnabled()) {
+            if (AsyncGroupHeaderViewInflation.isEnabled()) {
+                mChildrenContainer.updateGroupHeaderExpandState();
+            } else {
                 // We create the header from the background thread instead
                 mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
                         isConversation());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt
index 8889a10..bf37036 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt
@@ -24,6 +24,11 @@
 /** Helper for reading or using the minimalism prototype flag state. */
 @Suppress("NOTHING_TO_INLINE")
 object NotificationMinimalismPrototype {
+
+    val version: Int by lazy {
+        SystemProperties.getInt("persist.notification_minimalism_prototype.version", 2)
+    }
+
     object V1 {
         /** The aconfig flag name */
         const val FLAG_NAME = Flags.FLAG_NOTIFICATION_MINIMALISM_PROTOTYPE
@@ -35,7 +40,7 @@
         /** Is the heads-up cycling animation enabled */
         @JvmStatic
         inline val isEnabled
-            get() = Flags.notificationMinimalismPrototype()
+            get() = Flags.notificationMinimalismPrototype() && version == 1
 
         /**
          * the prototype will now show seen notifications on the locked shade by default, but this
@@ -76,4 +81,45 @@
         @JvmStatic
         inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
     }
+    object V2 {
+        const val FLAG_NAME = Flags.FLAG_NOTIFICATION_MINIMALISM_PROTOTYPE
+
+        /** A token used for dependency declaration */
+        val token: FlagToken
+            get() = FlagToken(FLAG_NAME, isEnabled)
+
+        /** Is the heads-up cycling animation enabled */
+        @JvmStatic
+        inline val isEnabled
+            get() = Flags.notificationMinimalismPrototype() && version == 2
+
+        /**
+         * The prototype will (by default) use a promoter to ensure that the top unseen notification
+         * is not grouped, but this property read allows that behavior to be disabled.
+         */
+        val ungroupTopUnseen: Boolean
+            get() =
+                if (isUnexpectedlyInLegacyMode()) false
+                else
+                    SystemProperties.getBoolean(
+                        "persist.notification_minimalism_prototype.ungroup_top_unseen",
+                        true
+                    )
+
+        /**
+         * Called to ensure code is only run when the flag is enabled. This protects users from the
+         * unintended behaviors caused by accidentally running new logic, while also crashing on an
+         * eng build to ensure that the refactor author catches issues in testing.
+         */
+        @JvmStatic
+        inline fun isUnexpectedlyInLegacyMode() =
+            RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+        /**
+         * Called to ensure code is only run when the flag is disabled. This will throw an exception
+         * if the flag is enabled to ensure that the refactor author catches issues in testing.
+         */
+        @JvmStatic
+        inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 92c597c..48796d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -439,6 +439,15 @@
         Trace.endSection();
     }
 
+    /**
+     * Update the expand state of the group header.
+     */
+    public void updateGroupHeaderExpandState() {
+        if (mGroupHeaderWrapper != null) {
+            mGroupHeaderWrapper.setExpanded(mChildrenExpanded);
+        }
+    }
+
     private void removeGroupHeader() {
         if (mGroupHeader == null) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index a446745..749c241 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -72,6 +72,9 @@
      */
     private var maxNotificationsExcludesMedia = false
 
+    /** Whether we allow keyguard to show less important notifications above the shelf. */
+    private var limitLockScreenToImportant = false
+
     /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */
     private var dividerHeight by notNull<Float>()
 
@@ -85,6 +88,14 @@
         updateResources()
     }
 
+    private fun allowedByPolicy(stackHeight: StackHeight): Boolean =
+        if (limitLockScreenToImportant && stackHeight.includesLessImportantNotification) {
+            log { "\tallowedByPolicy = false" }
+            false
+        } else {
+            true
+        }
+
     /**
      * Returns whether notifications and (shelf if visible) can fit in total space available.
      * [shelfSpace] is extra vertical space allowed for the shelf to overlap the lock icon.
@@ -183,11 +194,12 @@
         log { "\tGet maxNotifWithoutSavingSpace ---" }
         val maxNotifWithoutSavingSpace =
             stackHeightSequence.lastIndexWhile { heightResult ->
-                canStackFitInSpace(
-                    heightResult,
-                    notifSpace = notifSpace,
-                    shelfSpace = shelfSpace
-                ) == FitResult.FIT
+                allowedByPolicy(heightResult) &&
+                    canStackFitInSpace(
+                        heightResult,
+                        notifSpace = notifSpace,
+                        shelfSpace = shelfSpace
+                    ) == FitResult.FIT
             }
 
         // How many notifications we can show at heightWithoutLockscreenConstraints
@@ -212,11 +224,12 @@
             saveSpaceOnLockscreen = true
             maxNotifications =
                 stackHeightSequence.lastIndexWhile { heightResult ->
-                    canStackFitInSpace(
-                        heightResult,
-                        notifSpace = notifSpace,
-                        shelfSpace = shelfSpace
-                    ) != FitResult.NO_FIT
+                    allowedByPolicy(heightResult) &&
+                        canStackFitInSpace(
+                            heightResult,
+                            notifSpace = notifSpace,
+                            shelfSpace = shelfSpace
+                        ) != FitResult.NO_FIT
                 }
             log { "\t--- maxNotifications=$maxNotifications" }
         }
@@ -318,7 +331,10 @@
 
         // Float height of shelf (0 if shelf is not showing), and space before the shelf that
         // changes during the lockscreen <=> full shade transition.
-        val shelfHeightWithSpaceBefore: Float
+        val shelfHeightWithSpaceBefore: Float,
+
+        /** Whether this stack height includes less at least one important notification. */
+        val includesLessImportantNotification: Boolean
     )
 
     private fun computeHeightPerNotificationLimit(
@@ -331,12 +347,15 @@
         var previous: ExpandableView? = null
         val onLockscreen = onLockscreen()
 
+        var includesLessImportantNotification = false
+
         // Only shelf. This should never happen, since we allow 1 view minimum (EmptyViewState).
         yield(
             StackHeight(
                 notifsHeight = 0f,
                 notifsHeightSavingSpace = 0f,
-                shelfHeightWithSpaceBefore = shelfHeight
+                shelfHeightWithSpaceBefore = shelfHeight,
+                includesLessImportantNotification = includesLessImportantNotification,
             )
         )
 
@@ -362,6 +381,18 @@
                     spaceBeforeShelf + shelfHeight
                 }
 
+            if (limitLockScreenToImportant && !includesLessImportantNotification) {
+                val bucket = (currentNotification as? ExpandableNotificationRow)?.entry?.bucket
+                includesLessImportantNotification =
+                    when (bucket) {
+                        null,
+                        BUCKET_MEDIA_CONTROLS,
+                        BUCKET_HEADS_UP,
+                        BUCKET_FOREGROUND_SERVICE -> false
+                        else -> true
+                    }
+            }
+
             log {
                 "\tcomputeHeightPerNotificationLimit i=$i notifs=$notifications " +
                     "notifsHeightSavingSpace=$notifsWithCollapsedHun" +
@@ -371,7 +402,8 @@
                 StackHeight(
                     notifsHeight = notifications,
                     notifsHeightSavingSpace = notifsWithCollapsedHun,
-                    shelfHeightWithSpaceBefore = shelfWithSpaceBefore
+                    shelfHeightWithSpaceBefore = shelfWithSpaceBefore,
+                    includesLessImportantNotification = includesLessImportantNotification,
                 )
             )
         }
@@ -382,11 +414,16 @@
             infiniteIfNegative(
                 if (NotificationMinimalismPrototype.V1.isEnabled) {
                     NotificationMinimalismPrototype.V1.maxNotifs
+                } else if (NotificationMinimalismPrototype.V2.isEnabled) {
+                    1
                 } else {
                     resources.getInteger(R.integer.keyguard_max_notification_count)
                 }
             )
-        maxNotificationsExcludesMedia = NotificationMinimalismPrototype.V1.isEnabled
+        maxNotificationsExcludesMedia =
+            NotificationMinimalismPrototype.V1.isEnabled ||
+                NotificationMinimalismPrototype.V2.isEnabled
+        limitLockScreenToImportant = NotificationMinimalismPrototype.V2.isEnabled
 
         dividerHeight =
             max(1f, resources.getDimensionPixelSize(R.dimen.notification_divider_height).toFloat())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
index dacafc4..db544ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
@@ -38,16 +38,6 @@
      */
     val shadeScrimBounds = MutableStateFlow<ShadeScrimBounds?>(null)
 
-    /**
-     * The y-coordinate in px of top of the contents of the notification stack. This value can be
-     * negative, if the stack is scrolled such that its top extends beyond the top edge of the
-     * screen.
-     */
-    val stackTop = MutableStateFlow(0f)
-
-    /** the bottom-most acceptable y-position for the bottom of the stack / shelf */
-    val stackBottom = MutableStateFlow(0f)
-
     /** the y position of the top of the HUN area */
     val headsUpTop = MutableStateFlow(0f)
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 365ead6..e7acbe3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -72,12 +72,6 @@
     val alphaForBrightnessMirror: StateFlow<Float> =
         placeholderRepository.alphaForBrightnessMirror.asStateFlow()
 
-    /** The y-coordinate in px of top of the contents of the notification stack. */
-    val stackTop: StateFlow<Float> = placeholderRepository.stackTop.asStateFlow()
-
-    /** The y-coordinate in px of bottom of the contents of the notification stack. */
-    val stackBottom: StateFlow<Float> = placeholderRepository.stackBottom.asStateFlow()
-
     /** The height of the keyguard's available space bounds */
     val constrainedAvailableSpace: StateFlow<Int> =
         placeholderRepository.constrainedAvailableSpace.asStateFlow()
@@ -121,16 +115,6 @@
         viewHeightRepository.headsUpHeight.value = height
     }
 
-    /** Sets the y-coord in px of the top of the contents of the notification stack. */
-    fun setStackTop(stackTop: Float) {
-        placeholderRepository.stackTop.value = stackTop
-    }
-
-    /** Sets the y-coord in px of the bottom of the contents of the notification stack. */
-    fun setStackBottom(stackBottom: Float) {
-        placeholderRepository.stackBottom.value = stackBottom
-    }
-
     /** Sets whether the notification stack is scrolled to the top. */
     fun setScrolledToTop(scrolledToTop: Boolean) {
         placeholderRepository.scrolledToTop.value = scrolledToTop
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 3c44713..622d8e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -79,8 +79,6 @@
         }
 
         launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
-        launch { viewModel.stackTop.collect { view.setStackTop(it) } }
-        launch { viewModel.stackBottom.collect { view.setStackBottom(it) } }
         launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
         launch { viewModel.headsUpTop.collect { view.setHeadsUpTop(it) } }
         launch { viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 082f6b6..6137381 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -130,10 +130,6 @@
     val maxAlpha: Flow<Float> =
         stackAppearanceInteractor.alphaForBrightnessMirror.dumpValue("maxAlpha")
 
-    /** The y-coordinate in px of top of the contents of the notification stack. */
-    val stackTop: Flow<Float> = stackAppearanceInteractor.stackTop.dumpValue("stackTop")
-    /** The y-coordinate in px of bottom of the contents of the notification stack. */
-    val stackBottom: Flow<Float> = stackAppearanceInteractor.stackBottom.dumpValue("stackBottom")
     /**
      * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any
      * further.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 736058a..97b86e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
-import com.android.systemui.common.shared.model.NotificationContainerBounds
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FeatureFlagsClassic
@@ -57,18 +56,6 @@
         interactor.setShadeScrimBounds(bounds)
     }
 
-    /** Notifies that the bounds of the notification placeholder have changed. */
-    fun onStackBoundsChanged(
-        top: Float,
-        bottom: Float,
-    ) {
-        keyguardInteractor.setNotificationContainerBounds(
-            NotificationContainerBounds(top = top, bottom = bottom)
-        )
-        interactor.setStackTop(top)
-        interactor.setStackBottom(bottom)
-    }
-
     /** Sets the available space */
     fun onConstrainedAvailableSpaceChanged(height: Int) {
         interactor.setConstrainedAvailableSpace(height)
diff --git a/packages/SystemUI/src/com/android/systemui/theme/CustomDynamicColors.java b/packages/SystemUI/src/com/android/systemui/theme/CustomDynamicColors.java
new file mode 100644
index 0000000..efeb2f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/theme/CustomDynamicColors.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2024 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.theme;
+
+import com.google.ux.material.libmonet.dynamiccolor.ContrastCurve;
+import com.google.ux.material.libmonet.dynamiccolor.DynamicColor;
+import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;
+import com.google.ux.material.libmonet.dynamiccolor.ToneDeltaPair;
+import com.google.ux.material.libmonet.dynamiccolor.TonePolarity;
+
+class CustomDynamicColors {
+    private final MaterialDynamicColors mMdc;
+
+    CustomDynamicColors(boolean isExtendedFidelity) {
+        this.mMdc = new MaterialDynamicColors(isExtendedFidelity);
+    }
+
+    // CLOCK COLORS
+
+    public DynamicColor widgetBackground() {
+        return new DynamicColor(
+                /* name= */ "widget_background",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 20.0 : 95.0,
+                /* isBackground= */ true,
+                /* background= */ null,
+                /* secondBackground= */ null,
+                /* contrastCurve= */ null,
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor clockHour() {
+        return new DynamicColor(
+                /* name= */ "clock_hour",
+                /* palette= */ (s) -> s.secondaryPalette,
+                /* tone= */ (s) -> s.isDark ? 30.0 : 60.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> widgetBackground(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 4.0, 5.0, 15.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(clockHour(), clockMinute(), 10.0, TonePolarity.DARKER,
+                        false));
+    }
+
+    public DynamicColor clockMinute() {
+        return new DynamicColor(
+                /* name= */ "clock_minute",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 40.0 : 90.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> widgetBackground(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 6.5, 10.0, 15.0),
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor clockSecond() {
+        return new DynamicColor(
+                /* name= */ "clock_second",
+                /* palette= */ (s) -> s.tertiaryPalette,
+                /* tone= */ (s) -> s.isDark ? 40.0 : 90.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> widgetBackground(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 5.0, 70.0, 11.0),
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor weatherTemp() {
+        return new DynamicColor(
+                /* name= */ "clock_second",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 55.0 : 80.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> widgetBackground(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 5.0, 70.0, 11.0),
+                /* toneDeltaPair= */ null);
+    }
+
+    // THEME APP ICONS
+
+    public DynamicColor themeApp() {
+        return new DynamicColor(
+                /* name= */ "theme_app",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 90.0 : 30.0, // Adjusted values
+                /* isBackground= */ true,
+                /* background= */ null,
+                /* secondBackground= */ null,
+                /* contrastCurve= */ null,
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor onThemeApp() {
+        return new DynamicColor(
+                /* name= */ "on_theme_app",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 40.0 : 80.0, // Adjusted values
+                /* isBackground= */ false,
+                /* background= */ (s) -> themeApp(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 7.0, 10.0),
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor themeAppRing() {
+        return new DynamicColor(
+                /* name= */ "theme_app_ring",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> 70.0,
+                /* isBackground= */ true,
+                /* background= */ null,
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0),
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor themeNotif() {
+        return new DynamicColor(
+                /* name= */ "theme_notif",
+                /* palette= */ (s) -> s.tertiaryPalette,
+                /* tone= */ (s) -> s.isDark ? 80.0 : 90.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> themeAppRing(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(themeNotif(), themeAppRing(), 10.0, TonePolarity.NEARER,
+                        false));
+    }
+
+    // SUPER G COLORS
+
+    public DynamicColor brandA() {
+        return new DynamicColor(
+                /* name= */ "brand_a",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 40.0 : 80.0,
+                /* isBackground= */ true,
+                /* background= */ (s) -> mMdc.surfaceContainerLow(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 7.0, 17.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(brandA(), brandB(), 10.0, TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor brandB() {
+        return new DynamicColor(
+                /* name= */ "brand_b",
+                /* palette= */ (s) -> s.secondaryPalette,
+                /* tone= */ (s) -> s.isDark ? 70.0 : 98.0,
+                /* isBackground= */ true,
+                /* background= */ (s) -> mMdc.surfaceContainerLow(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 3.0, 6.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(brandB(), brandC(), 10.0, TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor brandC() {
+        return new DynamicColor(
+                /* name= */ "brand_c",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> s.isDark ? 50.0 : 60.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> mMdc.surfaceContainerLow(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 4.0, 9.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(brandC(), brandD(), 10.0, TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor brandD() {
+        return new DynamicColor(
+                /* name= */ "brand_d",
+                /* palette= */ (s) -> s.tertiaryPalette,
+                /* tone= */ (s) -> s.isDark ? 59.0 : 90.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> mMdc.surfaceContainerLow(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 4.0, 13.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(brandD(), brandA(), 10.0, TonePolarity.NEARER, false));
+    }
+
+    // QUICK SETTING TIILES
+
+    public DynamicColor underSurface() {
+        return new DynamicColor(
+                /* name= */ "under_surface",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> 0.0,
+                /* isBackground= */ true,
+                /* background= */ null,
+                /* secondBackground= */ null,
+                /* contrastCurve= */ null,
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor shadeActive() {
+        return new DynamicColor(
+                /* name= */ "shade_active",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> 90.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> underSurface(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 4.5, 7.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(shadeActive(), shadeInactive(), 30.0, TonePolarity.LIGHTER,
+                        false));
+    }
+
+    public DynamicColor onShadeActive() {
+        return new DynamicColor(
+                /* name= */ "on_shade_active",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> 10.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> shadeActive(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(onShadeActive(), onShadeActiveVariant(), 20.0,
+                        TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor onShadeActiveVariant() {
+        return new DynamicColor(
+                /* name= */ "on_shade_active_variant",
+                /* palette= */ (s) -> s.primaryPalette,
+                /* tone= */ (s) -> 30.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> shadeActive(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(onShadeActiveVariant(), onShadeActive(), 20.0,
+                        TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor shadeInactive() {
+        return new DynamicColor(
+                /* name= */ "shade_inactive",
+                /* palette= */ (s) -> s.neutralPalette,
+                /* tone= */ (s) -> 20.0,
+                /* isBackground= */ true,
+                /* background= */ (s) -> underSurface(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0),
+                /* toneDeltaPair= */(s) -> new ToneDeltaPair(shadeInactive(), shadeDisabled(), 15.0,
+                TonePolarity.LIGHTER, false));
+    }
+
+    public DynamicColor onShadeInactive() {
+        return new DynamicColor(
+                /* name= */ "on_shade_inactive",
+                /* palette= */ (s) -> s.neutralVariantPalette,
+                /* tone= */ (s) -> 90.0,
+                /* isBackground= */ true,
+                /* background= */ (s) -> shadeInactive(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(onShadeInactive(), onShadeInactiveVariant(), 10.0,
+                        TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor onShadeInactiveVariant() {
+        return new DynamicColor(
+                /* name= */ "on_shade_inactive_variant",
+                /* palette= */ (s) -> s.neutralVariantPalette,
+                /* tone= */ (s) -> 80.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> shadeInactive(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0),
+                /* toneDeltaPair= */
+                (s) -> new ToneDeltaPair(onShadeInactiveVariant(), onShadeInactive(), 10.0,
+                        TonePolarity.NEARER, false));
+    }
+
+    public DynamicColor shadeDisabled() {
+        return new DynamicColor(
+                /* name= */ "shade_disabled",
+                /* palette= */ (s) -> s.neutralPalette,
+                /* tone= */ (s) -> 4.0,
+                /* isBackground= */ false,
+                /* background= */ (s) -> underSurface(),
+                /* secondBackground= */ null,
+                /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0),
+                /* toneDeltaPair= */ null);
+    }
+
+    public DynamicColor overviewBackground() {
+        return new DynamicColor(
+                /* name= */ "overview_background",
+                /* palette= */ (s) -> s.neutralVariantPalette,
+                /* tone= */ (s) -> s.isDark ? 80.0 : 35.0,
+                /* isBackground= */ true,
+                /* background= */ null,
+                /* secondBackground= */ null,
+                /* contrastCurve= */null,
+                /* toneDeltaPair= */ null);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
index a983d2f..3518759 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
+++ b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
@@ -103,5 +103,33 @@
                 Pair.create("on_tertiary_fixed_variant", mdc.onTertiaryFixedVariant()),
             )
         }
+
+        @JvmStatic
+        fun getCustomColorsMapped(isExtendedFidelity: Boolean): List<Pair<String, DynamicColor>> {
+            val customMdc = CustomDynamicColors(isExtendedFidelity)
+            return arrayListOf(
+                Pair.create("widget_background", customMdc.widgetBackground()),
+                Pair.create("clock_hour", customMdc.clockHour()),
+                Pair.create("clock_minute", customMdc.clockMinute()),
+                Pair.create("clock_second", customMdc.weatherTemp()),
+                Pair.create("theme_app", customMdc.themeApp()),
+                Pair.create("on_theme_app", customMdc.onThemeApp()),
+                Pair.create("theme_app_ring", customMdc.themeAppRing()),
+                Pair.create("on_theme_app_ring", customMdc.themeNotif()),
+                Pair.create("brand_a", customMdc.brandA()),
+                Pair.create("brand_b", customMdc.brandB()),
+                Pair.create("brand_c", customMdc.brandC()),
+                Pair.create("brand_d", customMdc.brandD()),
+                Pair.create("under_surface", customMdc.underSurface()),
+                Pair.create("shade_active", customMdc.shadeActive()),
+                Pair.create("on_shade_active", customMdc.onShadeActive()),
+                Pair.create("on_shade_active_variant", customMdc.onShadeActiveVariant()),
+                Pair.create("shade_inactive", customMdc.shadeInactive()),
+                Pair.create("on_shade_inactive", customMdc.onShadeInactive()),
+                Pair.create("on_shade_inactive_variant", customMdc.onShadeInactiveVariant()),
+                Pair.create("shade_disabled", customMdc.shadeDisabled()),
+                Pair.create("overview_background", customMdc.overviewBackground())
+            )
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 5c3bbb7..d256c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -56,6 +56,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
@@ -84,6 +85,7 @@
 import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.settings.SecureSettings;
 
+import com.google.ux.material.libmonet.dynamiccolor.DynamicColor;
 import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;
 
 import org.json.JSONException;
@@ -631,29 +633,33 @@
 
     protected FabricatedOverlay createDynamicOverlay() {
         FabricatedOverlay overlay = newFabricatedOverlay("dynamic");
-        assignDynamicPaletteToOverlay(overlay, true /* isDark */);
-        assignDynamicPaletteToOverlay(overlay, false /* isDark */);
-        assignFixedColorsToOverlay(overlay);
+        //Themed Colors
+        assignColorsToOverlay(overlay, DynamicColors.allDynamicColorsMapped(mIsFidelityEnabled),
+                false);
+        // Fixed Colors
+        assignColorsToOverlay(overlay, DynamicColors.getFixedColorsMapped(mIsFidelityEnabled),
+                true);
+        //Custom Colors
+        assignColorsToOverlay(overlay, DynamicColors.getCustomColorsMapped(mIsFidelityEnabled),
+                false);
         return overlay;
     }
 
-    private void assignDynamicPaletteToOverlay(FabricatedOverlay overlay, boolean isDark) {
-        String suffix = isDark ? "dark" : "light";
-        ColorScheme scheme = isDark ? mDarkColorScheme : mLightColorScheme;
-        DynamicColors.allDynamicColorsMapped(mIsFidelityEnabled).forEach(p -> {
-            String resourceName = "android:color/system_" + p.first + "_" + suffix;
-            int colorValue = p.second.getArgb(scheme.getMaterialScheme());
-            overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue,
-                    null /* configuration */);
-        });
-    }
+    private void assignColorsToOverlay(FabricatedOverlay overlay,
+            List<Pair<String, DynamicColor>> colors, Boolean isFixed) {
+        colors.forEach(p -> {
+            String prefix = "android:color/system_" + p.first;
 
-    private void assignFixedColorsToOverlay(FabricatedOverlay overlay) {
-        DynamicColors.getFixedColorsMapped(mIsFidelityEnabled).forEach(p -> {
-            String resourceName = "android:color/system_" + p.first;
-            int colorValue = p.second.getArgb(mLightColorScheme.getMaterialScheme());
-            overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue,
-                    null /* configuration */);
+            if (isFixed) {
+                overlay.setResourceValue(prefix, TYPE_INT_COLOR_ARGB8,
+                        p.second.getArgb(mLightColorScheme.getMaterialScheme()), null);
+                return;
+            }
+
+            overlay.setResourceValue(prefix + "_light", TYPE_INT_COLOR_ARGB8,
+                    p.second.getArgb(mLightColorScheme.getMaterialScheme()), null);
+            overlay.setResourceValue(prefix + "_dark", TYPE_INT_COLOR_ARGB8,
+                    p.second.getArgb(mDarkColorScheme.getMaterialScheme()), null);
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
index bfed0c4..0a1724c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
@@ -70,8 +70,10 @@
     val isGuestUserResetting: Boolean = repository.isGuestUserResetting
 
     init {
-        resumeSessionReceiver.register()
-        resetOrExitSessionReceiver.register()
+        if (applicationContext.userId == UserHandle.USER_SYSTEM) {
+            resumeSessionReceiver.register()
+            resetOrExitSessionReceiver.register()
+        }
     }
 
     /** Notifies that the device has finished booting. */
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
index 46ce5f2..1ec86a4 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
@@ -97,3 +97,12 @@
 fun <A, B, R> combineFlows(flow1: Flow<A>, flow2: Flow<B>, bifunction: (A, B) -> R): Flow<R> {
     return combine(flow1, flow2, bifunction)
 }
+
+fun <A, B, C, R> combineFlows(
+    flow1: Flow<A>,
+    flow2: Flow<B>,
+    flow3: Flow<C>,
+    trifunction: (A, B, C) -> R
+): Flow<R> {
+    return combine(flow1, flow2, flow3, trifunction)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index bc6c459..5361cef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -35,9 +35,13 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.flags.DisableSceneContainer;
+import com.android.systemui.flags.EnableSceneContainer;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -50,6 +54,7 @@
 import com.android.systemui.util.sensors.ThresholdSensor;
 import com.android.systemui.util.time.FakeSystemClock;
 
+import kotlinx.coroutines.flow.MutableStateFlow;
 import kotlinx.coroutines.flow.StateFlowKt;
 
 import org.junit.Before;
@@ -89,6 +94,14 @@
     private SelectedUserInteractor mSelectedUserInteractor;
     @Mock
     private CommunalInteractor mCommunalInteractor;
+    @Mock
+    private DeviceEntryInteractor mDeviceEntryInteractor;
+    private final MutableStateFlow<Boolean> mIsDeviceEntered =
+            StateFlowKt.MutableStateFlow(false);
+    @Mock
+    private SceneContainerOcclusionInteractor mSceneContainerOcclusionInteractor;
+    private final MutableStateFlow<Boolean> mIsInvisibleDueToOcclusion =
+            StateFlowKt.MutableStateFlow(false);
     private final DockManagerFake mDockManager = new DockManagerFake();
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
@@ -99,15 +112,21 @@
 
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
         when(mShadeInteractor.isQsExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
 
+        when(mDeviceEntryInteractor.isDeviceEntered()).thenReturn(mIsDeviceEntered);
+        when(mSceneContainerOcclusionInteractor.getInvisibleDueToOcclusion()).thenReturn(
+                mIsInvisibleDueToOcclusion);
+
         mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager,
                 mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor,
                 mStatusBarStateController, mKeyguardStateController,
                 () -> mShadeInteractor, mBatteryController,
                 mDockManager, mFakeExecutor,
                 mJavaAdapter, mFakeSystemClock, () -> mSelectedUserInteractor,
-                () -> mCommunalInteractor
+                () -> mCommunalInteractor, () -> mDeviceEntryInteractor,
+                () -> mSceneContainerOcclusionInteractor
         );
         mFalsingCollector.init();
     }
@@ -189,7 +208,8 @@
     }
 
     @Test
-    public void testRegisterSensor_OccludingActivity() {
+    @DisableSceneContainer
+    public void testRegisterSensor_OccludingActivity_sceneContainerDisabled() {
         when(mKeyguardStateController.isOccluded()).thenReturn(true);
 
         ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor =
@@ -203,6 +223,21 @@
     }
 
     @Test
+    @EnableSceneContainer
+    public void testRegisterSensor_OccludingActivity_sceneContainerEnabled() {
+        mIsInvisibleDueToOcclusion.setValue(true);
+
+        ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor =
+                ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
+        verify(mStatusBarStateController).addCallback(stateListenerArgumentCaptor.capture());
+
+        mFalsingCollector.onScreenTurningOn();
+        reset(mProximitySensor);
+        stateListenerArgumentCaptor.getValue().onStateChanged(StatusBarState.SHADE);
+        verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
+    }
+
+    @Test
     public void testPassThroughEnterKeyEvent() {
         KeyEvent enterDown = KeyEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER,
                 0, 0, 0, 0, 0, 0, 0, "");
@@ -280,7 +315,8 @@
     }
 
     @Test
-    public void testAvoidUnlocked() {
+    @DisableSceneContainer
+    public void testAvoidUnlocked_sceneContainerDisabled() {
         MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
         MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
 
@@ -296,6 +332,23 @@
     }
 
     @Test
+    @EnableSceneContainer
+    public void testAvoidUnlocked_sceneContainerEnabled() {
+        MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+
+        mIsDeviceEntered.setValue(true);
+
+        // Nothing passed initially
+        mFalsingCollector.onTouchEvent(down);
+        verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+
+        // Up event would normally flush the up event, but doesn't.
+        mFalsingCollector.onTouchEvent(up);
+        verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+    }
+
+    @Test
     public void testGestureWhenDozing() {
         // We check the FalsingManager for taps during the transition to AoD (dozing=true,
         // pulsing=false), so the FalsingCollector needs to continue to analyze events that occur
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
index d0d9891..8a5af09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
@@ -40,7 +40,6 @@
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.runner.RunWith
 
 @SmallTest
@@ -1149,7 +1148,6 @@
      *
      * In STL there is no guarantee that transitions settle in Idle before continuing.
      */
-    @Ignore("Suffers from a race condition that will be fixed in followup CL")
     @Test
     fun transition_from_ls_scene_interrupted_by_other_from_ls_transition() =
         testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
index e56a253..265ade3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
@@ -172,20 +172,20 @@
     fun testOnRemovedForCurrent_callsListener() {
         // GIVEN a media was removed for main user
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
-        mediaDataFilter.onMediaDataRemoved(KEY)
+        mediaDataFilter.onMediaDataRemoved(KEY, false)
 
         // THEN we should tell the listener
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
     fun testOnRemovedForGuest_doesNotCallListener() {
         // GIVEN a media was removed for guest user
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
-        mediaDataFilter.onMediaDataRemoved(KEY)
+        mediaDataFilter.onMediaDataRemoved(KEY, false)
 
         // THEN we should NOT tell the listener
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
     }
 
     @Test
@@ -197,7 +197,7 @@
         setUser(USER_GUEST)
 
         // THEN we should remove the main user's media
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -230,7 +230,7 @@
         setPrivateProfileUnavailable()
 
         // THEN we should add the private profile media
-        verify(listener).onMediaDataRemoved(eq(KEY_ALT))
+        verify(listener).onMediaDataRemoved(eq(KEY_ALT), eq(false))
     }
 
     @Test
@@ -360,7 +360,7 @@
     @Test
     fun testOnNotificationRemoved_doesntHaveMedia() {
         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
-        mediaDataFilter.onMediaDataRemoved(KEY)
+        mediaDataFilter.onMediaDataRemoved(KEY, false)
         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
index 5a2d22d..99bf2db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
@@ -346,7 +346,7 @@
         // THEN it is removed and listeners are informed
         foregroundExecutor.advanceClockToLast()
         foregroundExecutor.runAllReady()
-        verify(listener).onMediaDataRemoved(PACKAGE_NAME)
+        verify(listener).onMediaDataRemoved(PACKAGE_NAME, false)
     }
 
     @Test
@@ -532,7 +532,7 @@
         addNotificationAndLoad()
         val data = mediaDataCaptor.value
         mediaDataManager.onNotificationRemoved(KEY)
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
     }
 
@@ -777,7 +777,7 @@
                 eq(false)
             )
         assertThat(mediaDataCaptor.value.resumption).isTrue()
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), eq(false))
         // WHEN the second is removed
         mediaDataManager.onNotificationRemoved(KEY_2)
         // THEN the data is for resumption and the second key is removed
@@ -791,7 +791,7 @@
                 eq(false)
             )
         assertThat(mediaDataCaptor.value.resumption).isTrue()
-        verify(listener).onMediaDataRemoved(eq(KEY_2))
+        verify(listener).onMediaDataRemoved(eq(KEY_2), eq(false))
     }
 
     @Test
@@ -816,7 +816,7 @@
         mediaDataManager.onNotificationRemoved(KEY)
 
         // THEN the media data is removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -866,7 +866,7 @@
         mediaDataManager.onNotificationRemoved(KEY)
 
         // THEN the media data is removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -905,7 +905,7 @@
         assertThat(mediaDataCaptor.value.isPlaying).isFalse()
 
         // And the oldest resume control was removed
-        verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"))
+        verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"), eq(false))
     }
 
     fun testOnNotificationRemoved_lockDownMode() {
@@ -915,7 +915,7 @@
         val data = mediaDataCaptor.value
         mediaDataManager.onNotificationRemoved(KEY)
 
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
         verify(logger, never())
             .logActiveConvertedToResume(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
@@ -1148,7 +1148,7 @@
         mediaDataManager.setMediaResumptionEnabled(false)
 
         // THEN the resume controls are dismissed
-        verify(listener).onMediaDataRemoved(eq(PACKAGE_NAME))
+        verify(listener).onMediaDataRemoved(eq(PACKAGE_NAME), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
     }
 
@@ -1156,19 +1156,19 @@
     fun testDismissMedia_listenerCalled() {
         addNotificationAndLoad()
         val data = mediaDataCaptor.value
-        val removed = mediaDataManager.dismissMediaData(KEY, 0L)
+        val removed = mediaDataManager.dismissMediaData(KEY, 0L, true)
         assertThat(removed).isTrue()
 
         foregroundExecutor.advanceClockToLast()
         foregroundExecutor.runAllReady()
 
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
     }
 
     @Test
     fun testDismissMedia_keyDoesNotExist_returnsFalse() {
-        val removed = mediaDataManager.dismissMediaData(KEY, 0L)
+        val removed = mediaDataManager.dismissMediaData(KEY, 0L, true)
         assertThat(removed).isFalse()
     }
 
@@ -2077,7 +2077,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It remains as a regular player
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
     }
@@ -2093,7 +2093,7 @@
         mediaDataManager.onNotificationRemoved(KEY)
 
         // It is fully removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2146,7 +2146,7 @@
         mediaDataManager.onNotificationRemoved(KEY)
 
         // It remains as a regular player
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
     }
@@ -2199,7 +2199,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It is fully removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2253,7 +2253,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It is fully removed.
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(
@@ -2279,7 +2279,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It is fully removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2329,7 +2329,7 @@
         mediaDataManager.onNotificationRemoved(KEY)
 
         // We still make sure to remove it
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
index bb5b572..dd05a0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
@@ -202,24 +202,24 @@
     @Test
     public void mediaDataRemoved() {
         // WHEN media data is removed without first receiving device or data
-        mManager.onMediaDataRemoved(KEY);
+        mManager.onMediaDataRemoved(KEY, false);
         // THEN a removed event isn't emitted
-        verify(mListener, never()).onMediaDataRemoved(eq(KEY));
+        verify(mListener, never()).onMediaDataRemoved(eq(KEY), anyBoolean());
     }
 
     @Test
     public void mediaDataRemovedAfterMediaEvent() {
         mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
                 0 /* receivedSmartspaceCardLatency */, false /* isSsReactivated */);
-        mManager.onMediaDataRemoved(KEY);
-        verify(mListener).onMediaDataRemoved(eq(KEY));
+        mManager.onMediaDataRemoved(KEY, false);
+        verify(mListener).onMediaDataRemoved(eq(KEY), eq(false));
     }
 
     @Test
     public void mediaDataRemovedAfterDeviceEvent() {
         mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
-        mManager.onMediaDataRemoved(KEY);
-        verify(mListener).onMediaDataRemoved(eq(KEY));
+        mManager.onMediaDataRemoved(KEY, false);
+        verify(mListener).onMediaDataRemoved(eq(KEY), eq(false));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index 77ad263..35eefd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -205,9 +205,9 @@
 
             assertThat(currentMedia).containsExactly(mediaCommonModel)
 
-            mediaDataFilter.onMediaDataRemoved(KEY)
+            mediaDataFilter.onMediaDataRemoved(KEY, false)
 
-            verify(listener).onMediaDataRemoved(eq(KEY))
+            verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
             assertThat(currentMedia).doesNotContain(mediaCommonModel)
         }
 
@@ -218,9 +218,9 @@
 
             // GIVEN a media was removed for guest user
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
-            mediaDataFilter.onMediaDataRemoved(KEY)
+            mediaDataFilter.onMediaDataRemoved(KEY, false)
 
-            verify(listener, never()).onMediaDataRemoved(eq(KEY))
+            verify(listener, never()).onMediaDataRemoved(eq(KEY), eq(false))
             assertThat(currentMedia).isEmpty()
         }
 
@@ -239,7 +239,7 @@
             setUser(USER_GUEST)
 
             // THEN we should remove the main user's media
-            verify(listener).onMediaDataRemoved(eq(KEY))
+            verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
             assertThat(currentMedia).isEmpty()
         }
 
@@ -291,7 +291,7 @@
 
             val mediaLoadedStatesModel = MediaDataLoadingModel.Loaded(dataMain.instanceId)
             // THEN we should remove the private profile media
-            verify(listener).onMediaDataRemoved(eq(KEY_ALT))
+            verify(listener).onMediaDataRemoved(eq(KEY_ALT), eq(false))
             assertThat(currentMedia)
                 .containsExactly(MediaCommonModel.MediaControl(mediaLoadedStatesModel))
         }
@@ -502,7 +502,7 @@
             val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
 
             mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
-            mediaDataFilter.onMediaDataRemoved(KEY)
+            mediaDataFilter.onMediaDataRemoved(KEY, false)
             assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData))
                 .isFalse()
             assertThat(hasAnyMedia(selectedUserEntries)).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 1de7ee3..5791826 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -384,7 +384,7 @@
         // THEN it is removed and listeners are informed
         foregroundExecutor.advanceClockToLast()
         foregroundExecutor.runAllReady()
-        verify(listener).onMediaDataRemoved(PACKAGE_NAME)
+        verify(listener).onMediaDataRemoved(PACKAGE_NAME, false)
     }
 
     @Test
@@ -567,7 +567,7 @@
         addNotificationAndLoad()
         val data = mediaDataCaptor.value
         mediaDataProcessor.onNotificationRemoved(KEY)
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
     }
 
@@ -812,7 +812,7 @@
                 eq(false)
             )
         assertThat(mediaDataCaptor.value.resumption).isTrue()
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
         // WHEN the second is removed
         mediaDataProcessor.onNotificationRemoved(KEY_2)
         // THEN the data is for resumption and the second key is removed
@@ -826,7 +826,7 @@
                 eq(false)
             )
         assertThat(mediaDataCaptor.value.resumption).isTrue()
-        verify(listener).onMediaDataRemoved(eq(KEY_2))
+        verify(listener).onMediaDataRemoved(eq(KEY_2), eq(false))
     }
 
     @Test
@@ -851,7 +851,7 @@
         mediaDataProcessor.onNotificationRemoved(KEY)
 
         // THEN the media data is removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -901,7 +901,7 @@
         mediaDataProcessor.onNotificationRemoved(KEY)
 
         // THEN the media data is removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -940,7 +940,7 @@
         assertThat(mediaDataCaptor.value.isPlaying).isFalse()
 
         // And the oldest resume control was removed
-        verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"))
+        verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"), eq(false))
     }
 
     fun testOnNotificationRemoved_lockDownMode() {
@@ -950,7 +950,7 @@
         val data = mediaDataCaptor.value
         mediaDataProcessor.onNotificationRemoved(KEY)
 
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger, never())
             .logActiveConvertedToResume(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
@@ -1183,7 +1183,7 @@
         mediaDataProcessor.setMediaResumptionEnabled(false)
 
         // THEN the resume controls are dismissed
-        verify(listener).onMediaDataRemoved(eq(PACKAGE_NAME))
+        verify(listener).onMediaDataRemoved(eq(PACKAGE_NAME), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
     }
 
@@ -1191,19 +1191,19 @@
     fun testDismissMedia_listenerCalled() {
         addNotificationAndLoad()
         val data = mediaDataCaptor.value
-        val removed = mediaDataProcessor.dismissMediaData(KEY, 0L)
+        val removed = mediaDataProcessor.dismissMediaData(KEY, 0L, true)
         assertThat(removed).isTrue()
 
         foregroundExecutor.advanceClockToLast()
         foregroundExecutor.runAllReady()
 
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
     }
 
     @Test
     fun testDismissMedia_keyDoesNotExist_returnsFalse() {
-        val removed = mediaDataProcessor.dismissMediaData(KEY, 0L)
+        val removed = mediaDataProcessor.dismissMediaData(KEY, 0L, true)
         assertThat(removed).isFalse()
     }
 
@@ -2102,7 +2102,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It remains as a regular player
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
     }
@@ -2118,7 +2118,7 @@
         mediaDataProcessor.onNotificationRemoved(KEY)
 
         // It is fully removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2171,7 +2171,7 @@
         mediaDataProcessor.onNotificationRemoved(KEY)
 
         // It remains as a regular player
-        verify(listener, never()).onMediaDataRemoved(eq(KEY))
+        verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
     }
@@ -2224,7 +2224,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It is fully removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2278,7 +2278,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It is fully removed.
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(
@@ -2304,7 +2304,7 @@
         sessionCallbackCaptor.value.invoke(KEY)
 
         // It is fully removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
         verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
         verify(listener, never())
             .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2354,7 +2354,7 @@
         mediaDataProcessor.onNotificationRemoved(KEY)
 
         // We still make sure to remove it
-        verify(listener).onMediaDataRemoved(eq(KEY))
+        verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index a447e44..befe64c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -60,6 +60,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.Mockito.any
@@ -158,7 +159,8 @@
 
     @Test
     fun removeUnknown() {
-        manager.onMediaDataRemoved("unknown")
+        manager.onMediaDataRemoved("unknown", false)
+        verify(listener, never()).onKeyRemoved(eq(KEY), anyBoolean())
     }
 
     @Test
@@ -170,7 +172,7 @@
     @Test
     fun loadAndRemoveMediaData() {
         manager.onMediaDataLoaded(KEY, null, mediaData)
-        manager.onMediaDataRemoved(KEY)
+        manager.onMediaDataRemoved(KEY, false)
         fakeBgExecutor.runAllReady()
         verify(lmm).unregisterCallback(any())
         verify(muteAwaitManager).stopListening()
@@ -386,9 +388,9 @@
     fun listenerReceivesKeyRemoved() {
         manager.onMediaDataLoaded(KEY, null, mediaData)
         // WHEN the notification is removed
-        manager.onMediaDataRemoved(KEY)
+        manager.onMediaDataRemoved(KEY, true)
         // THEN the listener receives key removed event
-        verify(listener).onKeyRemoved(eq(KEY))
+        verify(listener).onKeyRemoved(eq(KEY), eq(true))
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
index 5a3c220..030bca2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
@@ -165,10 +165,10 @@
 
     @Test
     fun noMediaSession_removedEventNotFiltered() {
-        filter.onMediaDataRemoved(KEY)
+        filter.onMediaDataRemoved(KEY, false)
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
-        verify(mediaListener).onMediaDataRemoved(eq(KEY))
+        verify(mediaListener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -193,11 +193,11 @@
         whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
         sessionListener.onActiveSessionsChanged(controllers)
         // WHEN a removed event is received
-        filter.onMediaDataRemoved(KEY)
+        filter.onMediaDataRemoved(KEY, false)
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the event is not filtered
-        verify(mediaListener).onMediaDataRemoved(eq(KEY))
+        verify(mediaListener).onMediaDataRemoved(eq(KEY), eq(false))
     }
 
     @Test
@@ -294,7 +294,7 @@
                 anyBoolean()
             )
         // AND there should be a removed event for key2
-        verify(mediaListener).onMediaDataRemoved(eq(key2))
+        verify(mediaListener).onMediaDataRemoved(eq(key2), eq(false))
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
index 3cc65c9..cdbf9d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
@@ -166,12 +166,12 @@
     @Test
     fun testOnMediaDataRemoved_unregistersPlaybackListener() {
         mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
-        mediaTimeoutListener.onMediaDataRemoved(KEY)
+        mediaTimeoutListener.onMediaDataRemoved(KEY, false)
         verify(mediaController).unregisterCallback(anyObject())
 
         // Ignores duplicate requests
         clearInvocations(mediaController)
-        mediaTimeoutListener.onMediaDataRemoved(KEY)
+        mediaTimeoutListener.onMediaDataRemoved(KEY, false)
         verify(mediaController, never()).unregisterCallback(anyObject())
     }
 
@@ -181,7 +181,7 @@
         mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
         assertThat(executor.numPending()).isEqualTo(1)
         // WHEN the media is removed
-        mediaTimeoutListener.onMediaDataRemoved(KEY)
+        mediaTimeoutListener.onMediaDataRemoved(KEY, false)
         // THEN the timeout runnable is cancelled
         assertThat(executor.numPending()).isEqualTo(0)
     }
@@ -398,7 +398,7 @@
         // WHEN we have a resume control
         testOnMediaDataLoaded_resumption_registersTimeout()
         // AND the media is removed
-        mediaTimeoutListener.onMediaDataRemoved(PACKAGE)
+        mediaTimeoutListener.onMediaDataRemoved(PACKAGE, false)
 
         // THEN the timeout runnable is cancelled
         assertThat(executor.numPending()).isEqualTo(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 0a5aace..3bb8b8f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -33,6 +33,8 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -74,12 +76,14 @@
 import kotlinx.coroutines.test.TestDispatcher
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito.anyLong
 import org.mockito.Mockito.floatThat
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
@@ -136,6 +140,9 @@
     private lateinit var testDispatcher: TestDispatcher
     private lateinit var mediaCarouselController: MediaCarouselController
 
+    private var originalResumeSetting =
+        Settings.Secure.getInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 1)
+
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
@@ -186,6 +193,15 @@
             )
     }
 
+    @After
+    fun tearDown() {
+        Settings.Secure.putInt(
+            context.contentResolver,
+            Settings.Secure.MEDIA_CONTROLS_RESUME,
+            originalResumeSetting
+        )
+    }
+
     @Test
     fun testPlayerOrdering() {
         // Test values: key, data, last active time
@@ -822,6 +838,7 @@
     @Test
     fun testKeyguardGone_showMediaCarousel() =
         kosmos.testScope.runTest {
+            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
             var updatedVisibility = false
             mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
             mediaCarouselController.mediaCarousel = mediaCarousel
@@ -844,6 +861,7 @@
     @Test
     fun keyguardShowing_notAllowedOnLockscreen_updateVisibility() {
         kosmos.testScope.runTest {
+            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
             var updatedVisibility = false
             mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
             mediaCarouselController.mediaCarousel = mediaCarousel
@@ -870,6 +888,7 @@
     @Test
     fun keyguardShowing_allowedOnLockscreen_updateVisibility() {
         kosmos.testScope.runTest {
+            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
             var updatedVisibility = false
             mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
             mediaCarouselController.mediaCarousel = mediaCarousel
@@ -968,6 +987,45 @@
         verify(panel).updateAnimatorDurationScale()
     }
 
+    @Test
+    fun swipeToDismiss_pausedAndResumeOff_userInitiated() {
+        // When resumption is disabled, paused media should be dismissed after being swiped away
+        Settings.Secure.putInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
+
+        val pausedMedia = DATA.copy(isPlaying = false)
+        listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, pausedMedia)
+        mediaCarouselController.onSwipeToDismiss()
+
+        // When it can be removed immediately on update
+        whenever(visualStabilityProvider.isReorderingAllowed).thenReturn(true)
+        val inactiveMedia = pausedMedia.copy(active = false)
+        listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, inactiveMedia)
+
+        // This is processed as a user-initiated dismissal
+        verify(debugLogger).logMediaRemoved(eq(PAUSED_LOCAL), eq(true))
+        verify(mediaDataManager).dismissMediaData(eq(PAUSED_LOCAL), anyLong(), eq(true))
+    }
+
+    @Test
+    fun swipeToDismiss_pausedAndResumeOff_delayed_userInitiated() {
+        // When resumption is disabled, paused media should be dismissed after being swiped away
+        Settings.Secure.putInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
+        mediaCarouselController.updateHostVisibility = {}
+
+        val pausedMedia = DATA.copy(isPlaying = false)
+        listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, pausedMedia)
+        mediaCarouselController.onSwipeToDismiss()
+
+        // When it can't be removed immediately on update
+        whenever(visualStabilityProvider.isReorderingAllowed).thenReturn(false)
+        val inactiveMedia = pausedMedia.copy(active = false)
+        listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, inactiveMedia)
+        visualStabilityCallback.value.onReorderingAllowed()
+
+        // This is processed as a user-initiated dismissal
+        verify(mediaDataManager).dismissMediaData(eq(PAUSED_LOCAL), anyLong(), eq(true))
+    }
+
     /**
      * Helper method when a configuration change occurs.
      *
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 83e4d31..0c9fee9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -1344,7 +1344,7 @@
         assertThat(dismiss.isEnabled).isEqualTo(true)
         dismiss.callOnClick()
         verify(logger).logLongPressDismiss(anyInt(), eq(PACKAGE), eq(instanceId))
-        verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong())
+        verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong(), eq(true))
     }
 
     @Test
@@ -1360,7 +1360,8 @@
     @Test
     fun player_dismissButtonClick_notInManager() {
         val mediaKey = "key for dismissal"
-        whenever(mediaDataManager.dismissMediaData(eq(mediaKey), anyLong())).thenReturn(false)
+        whenever(mediaDataManager.dismissMediaData(eq(mediaKey), anyLong(), eq(true)))
+            .thenReturn(false)
 
         player.attachPlayer(viewHolder)
         val state = mediaData.copy(notificationKey = KEY)
@@ -1369,8 +1370,8 @@
         assertThat(dismiss.isEnabled).isEqualTo(true)
         dismiss.callOnClick()
 
-        verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong())
-        verify(mediaCarouselController).removePlayer(eq(mediaKey), eq(false), eq(false))
+        verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong(), eq(true))
+        verify(mediaCarouselController).removePlayer(eq(mediaKey), eq(false), eq(false), eq(true))
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index ff7c970..8f8630e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -104,11 +104,11 @@
         listener.onMediaDataLoaded(mKey, mOldKey, mData, /* immediately= */true,
                 /* receivedSmartspaceCardLatency= */0, /* isSsReactived= */ false);
 
-        listener.onMediaDataRemoved(mKey);
+        listener.onMediaDataRemoved(mKey, false);
         verify(mDreamOverlayStateController, never()).removeComplication(any());
 
         when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
-        listener.onMediaDataRemoved(mKey);
+        listener.onMediaDataRemoved(mKey, false);
         verify(mDreamOverlayStateController).removeComplication(eq(mMediaEntryComplication));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt
index 9f0e67b..85cc88d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt
@@ -15,11 +15,13 @@
  */
 package com.android.systemui.monet
 
-import androidx.test.filters.SmallTest
 import android.testing.AndroidTestingRunner
 import android.util.Log
+import android.util.Pair
+import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.theme.DynamicColors
+import com.google.ux.material.libmonet.dynamiccolor.DynamicColor
 import com.google.ux.material.libmonet.hct.Hct
 import com.google.ux.material.libmonet.scheme.SchemeTonalSpot
 import java.io.File
@@ -81,6 +83,10 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class ColorSchemeTest : SysuiTestCase() {
+    private val defaultContrast = 0.0
+    private val defaultIsDark = false
+    private val defaultIsFidelity = false
+
     @Test
     fun generateThemeStyles() {
         val document = buildDoc<Any>()
@@ -107,7 +113,7 @@
                 }
 
                 val style = document.createElement(styleValue.name.lowercase())
-                val colorScheme = ColorScheme(sourceColor.toInt(), false, styleValue)
+                val colorScheme = ColorScheme(sourceColor.toInt(), defaultIsDark, styleValue)
 
                 style.appendChild(
                     document.createTextNode(
@@ -139,7 +145,7 @@
         document.appendWithBreak(resources)
 
         // shade colors
-        val colorScheme = ColorScheme(GOOGLE_BLUE, false)
+        val colorScheme = ColorScheme(GOOGLE_BLUE, defaultIsDark)
         arrayOf(
                 Triple("accent1", "Primary", colorScheme.accent1),
                 Triple("accent2", "Secondary", colorScheme.accent2),
@@ -162,24 +168,35 @@
 
         resources.appendWithBreak(document.createComment(commentRoles), 2)
 
-        // dynamic colors
-        arrayOf(false, true).forEach { isDark ->
-            val suffix = if (isDark) "_dark" else "_light"
-            val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), isDark, 0.5)
-            DynamicColors.allDynamicColorsMapped(false).forEach {
-                resources.createColorEntry(
-                    "system_${it.first}$suffix",
-                    it.second.getArgb(dynamicScheme)
-                )
+        fun generateDynamic(pairs: List<Pair<String, DynamicColor>>) {
+            arrayOf(false, true).forEach { isDark ->
+                val suffix = if (isDark) "_dark" else "_light"
+                val dynamicScheme =
+                    SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), isDark, defaultContrast)
+                pairs.forEach {
+                    resources.createColorEntry(
+                        "system_${it.first}$suffix",
+                        it.second.getArgb(dynamicScheme)
+                    )
+                }
             }
         }
 
+        // dynamic colors
+        generateDynamic(DynamicColors.allDynamicColorsMapped(defaultIsFidelity))
+
         // fixed colors
-        val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), false, 0.5)
-        DynamicColors.getFixedColorsMapped(false).forEach {
+        val dynamicScheme =
+            SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), defaultIsDark, defaultContrast)
+        DynamicColors.getFixedColorsMapped(defaultIsFidelity).forEach {
             resources.createColorEntry("system_${it.first}", it.second.getArgb(dynamicScheme))
         }
 
+        resources.appendWithBreak(document.createComment(commentRoles), 2)
+
+        // custom colors
+        generateDynamic(DynamicColors.getCustomColorsMapped(defaultIsFidelity))
+
         saveFile(document, "role_values.xml")
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
index 5e53fe1..5cd3f66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
@@ -23,17 +23,18 @@
 import android.testing.TestableContext
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.screenshot.proxy.SystemUiProxy
 import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.shared.system.ActivityManagerWrapper
 import com.android.systemui.statusbar.phone.CentralSurfaces
-import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestCoroutineScheduler
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
 
 @RunWith(AndroidTestingRunner::class)
 class ActionIntentExecutorTest : SysuiTestCase() {
@@ -44,8 +45,9 @@
     private val testableContext = TestableContext(mContext)
 
     private val activityManagerWrapper = mock<ActivityManagerWrapper>()
+    private val systemUiProxy = mock<SystemUiProxy>()
+
     private val displayTracker = mock<DisplayTracker>()
-    private val keyguardController = mock<ScreenshotKeyguardController>()
 
     private val actionIntentExecutor =
         ActionIntentExecutor(
@@ -53,12 +55,12 @@
             activityManagerWrapper,
             testScope,
             mainDispatcher,
+            systemUiProxy,
             displayTracker,
-            keyguardController,
         )
 
     @Test
-    @EnableFlags(Flags.FLAG_SCREENSHOT_ACTION_DISMISS_SYSTEM_WINDOWS)
+    @EnableFlags(Flags.FLAG_FIX_SCREENSHOT_ACTION_DISMISS_SYSTEM_WINDOWS)
     fun launchIntent_callsCloseSystemWindows() =
         testScope.runTest {
             val intent = Intent(Intent.ACTION_EDIT).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index ed7c956..a5e7a67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -995,9 +995,11 @@
                 .setResourceValue(any(String.class), eq(TYPE_INT_COLOR_ARGB8), anyInt(), eq(null));
         // All dynamic colors were added twice: light and dark them
         // All fixed colors were added once
+        // All custom dynamic tokens added twice
         verify(dynamic, times(
                 DynamicColors.allDynamicColorsMapped(false).size() * 2
-                        + DynamicColors.getFixedColorsMapped(false).size())
+                        + DynamicColors.getFixedColorsMapped(false).size()
+                        + DynamicColors.getCustomColorsMapped(false).size() * 2)
         ).setResourceValue(any(String.class), eq(TYPE_INT_COLOR_ARGB8), anyInt(), eq(null));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
index 948670f..01795e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.user.domain.interactor
 
 import android.app.admin.DevicePolicyManager
+import android.content.Context
 import android.content.pm.UserInfo
 import android.os.UserHandle
 import android.os.UserManager
@@ -59,6 +60,7 @@
     @Mock private lateinit var switchUser: (Int) -> Unit
     @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
     @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
+    @Mock private lateinit var otherContext: Context
 
     private lateinit var underTest: GuestUserInteractor
 
@@ -74,28 +76,30 @@
         repository = FakeUserRepository()
         repository.setUserInfos(ALL_USERS)
 
-        underTest =
-            GuestUserInteractor(
-                applicationContext = context,
-                applicationScope = scope,
-                mainDispatcher = IMMEDIATE,
-                backgroundDispatcher = IMMEDIATE,
-                manager = manager,
-                repository = repository,
-                deviceProvisionedController = deviceProvisionedController,
-                devicePolicyManager = devicePolicyManager,
-                refreshUsersScheduler =
-                    RefreshUsersScheduler(
-                        applicationScope = scope,
-                        mainDispatcher = IMMEDIATE,
-                        repository = repository,
-                    ),
-                uiEventLogger = uiEventLogger,
-                resumeSessionReceiver = resumeSessionReceiver,
-                resetOrExitSessionReceiver = resetOrExitSessionReceiver,
-            )
+        underTest = initGuestUserInteractor(context)
     }
 
+    private fun initGuestUserInteractor(context: Context) =
+        GuestUserInteractor(
+            applicationContext = context,
+            applicationScope = scope,
+            mainDispatcher = IMMEDIATE,
+            backgroundDispatcher = IMMEDIATE,
+            manager = manager,
+            repository = repository,
+            deviceProvisionedController = deviceProvisionedController,
+            devicePolicyManager = devicePolicyManager,
+            refreshUsersScheduler =
+                RefreshUsersScheduler(
+                    applicationScope = scope,
+                    mainDispatcher = IMMEDIATE,
+                    repository = repository,
+                ),
+            uiEventLogger = uiEventLogger,
+            resumeSessionReceiver = resumeSessionReceiver,
+            resetOrExitSessionReceiver = resetOrExitSessionReceiver,
+        )
+
     @Test
     fun registersBroadcastReceivers() {
         verify(resumeSessionReceiver).register()
@@ -103,6 +107,16 @@
     }
 
     @Test
+    fun registersBroadcastReceiversOnlyForSystemUser() {
+        for (i in 1..5) {
+            whenever(otherContext.userId).thenReturn(UserHandle.MIN_SECONDARY_USER_ID + i)
+            initGuestUserInteractor(otherContext)
+        }
+        verify(resumeSessionReceiver).register()
+        verify(resetOrExitSessionReceiver).register()
+    }
+
+    @Test
     fun onDeviceBootCompleted_allowedToAdd_createGuest() =
         runBlocking(IMMEDIATE) {
             setAllowedToAdd()
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index bc608c5..95cbb6b 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -221,7 +221,9 @@
     data: [
         ":framework-minus-apex.ravenwood.stats",
         ":framework-minus-apex.ravenwood.apis",
+        ":framework-minus-apex.ravenwood.keep_all",
         ":services.core.ravenwood.stats",
         ":services.core.ravenwood.apis",
+        ":services.core.ravenwood.keep_all",
     ],
 }
diff --git a/ravenwood/scripts/ravenwood-stats-collector.sh b/ravenwood/scripts/ravenwood-stats-collector.sh
index cf58bd2..43b61a4 100755
--- a/ravenwood/scripts/ravenwood-stats-collector.sh
+++ b/ravenwood/scripts/ravenwood-stats-collector.sh
@@ -18,8 +18,14 @@
 set -e
 
 # Output files
-stats=/tmp/ravenwood-stats-all.csv
-apis=/tmp/ravenwood-apis-all.csv
+out_dir=/tmp/ravenwood
+stats=$out_dir/ravenwood-stats-all.csv
+apis=$out_dir/ravenwood-apis-all.csv
+keep_all_dir=$out_dir/ravenwood-keep-all/
+
+rm -fr $out_dir
+mkdir -p $out_dir
+mkdir -p $keep_all_dir
 
 # Where the input files are.
 path=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/ravenwood-stats-checker/x86_64/
@@ -76,3 +82,7 @@
 
 collect_stats $stats
 collect_apis $apis
+
+cp *keep_all.txt $keep_all_dir
+echo "Keep all files created at:"
+find $keep_all_dir -type f
\ No newline at end of file
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 06a0297..71b16c3 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -172,8 +172,7 @@
         OnCrossProfileWidgetProvidersChangeListener {
     private static final String TAG = "AppWidgetServiceImpl";
 
-    private static final boolean DEBUG = false;
-    private static final boolean DEBUG_NULL_PROVIDER_INFO = Build.IS_DEBUGGABLE;
+    private static final boolean DEBUG = Build.IS_DEBUGGABLE;
 
     private static final String OLD_KEYGUARD_HOST_PACKAGE = "android";
     private static final String NEW_KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
@@ -1573,9 +1572,36 @@
                     Binder.getCallingUid(), callingPackage);
 
             if (widget != null && widget.provider != null && !widget.provider.zombie) {
-                return cloneIfLocalBinder(widget.provider.getInfoLocked(mContext));
+                final AppWidgetProviderInfo info = widget.provider.getInfoLocked(mContext);
+                if (info == null) {
+                    Slog.e(TAG, "getAppWidgetInfo() returns null because"
+                            + " widget.provider.getInfoLocked() returned null."
+                            + " appWidgetId=" + appWidgetId + " userId=" + userId
+                            + " widget=" + widget);
+                    return null;
+                }
+                final AppWidgetProviderInfo ret = cloneIfLocalBinder(info);
+                if (ret == null) {
+                    Slog.e(TAG, "getAppWidgetInfo() returns null because"
+                            + " cloneIfLocalBinder() returned null."
+                            + " appWidgetId=" + appWidgetId + " userId=" + userId
+                            + " widget=" + widget + " appWidgetProviderInfo=" + info);
+                }
+                return ret;
+            } else {
+                if (widget == null) {
+                    Slog.e(TAG, "getAppWidgetInfo() returns null because widget is null."
+                            + " appWidgetId=" + appWidgetId + " userId=" + userId);
+                } else if (widget.provider == null) {
+                    Slog.e(TAG, "getAppWidgetInfo() returns null because widget.provider is null."
+                            + " appWidgetId=" + appWidgetId + " userId=" + userId
+                            + " widget=" + widget);
+                } else {
+                    Slog.e(TAG, "getAppWidgetInfo() returns null because widget.provider is zombie."
+                            + " appWidgetId=" + appWidgetId + " userId=" + userId
+                            + " widget=" + widget);
+                }
             }
-
             return null;
         }
     }
@@ -2960,7 +2986,7 @@
             AppWidgetProviderInfo info = new AppWidgetProviderInfo();
             info.provider = providerId.componentName;
             info.providerInfo = ri.activityInfo;
-            if (DEBUG_NULL_PROVIDER_INFO) {
+            if (DEBUG) {
                 Objects.requireNonNull(ri.activityInfo);
             }
             return info;
@@ -2997,7 +3023,7 @@
             AppWidgetProviderInfo info = new AppWidgetProviderInfo();
             info.provider = providerId.componentName;
             info.providerInfo = activityInfo;
-            if (DEBUG_NULL_PROVIDER_INFO) {
+            if (DEBUG) {
                 Objects.requireNonNull(activityInfo);
             }
 
@@ -3575,7 +3601,7 @@
                             AppWidgetProviderInfo info = new AppWidgetProviderInfo();
                             info.provider = providerId.componentName;
                             info.providerInfo = providerInfo;
-                            if (DEBUG_NULL_PROVIDER_INFO) {
+                            if (DEBUG) {
                                 Objects.requireNonNull(providerInfo);
                             }
 
@@ -3594,7 +3620,7 @@
                             if (info != null) {
                                 info.provider = providerId.componentName;
                                 info.providerInfo = providerInfo;
-                                if (DEBUG_NULL_PROVIDER_INFO) {
+                                if (DEBUG) {
                                     Objects.requireNonNull(providerInfo);
                                 }
                                 provider.setInfoLocked(info);
@@ -4678,6 +4704,9 @@
                     }
                     if (newInfo != null) {
                         info = newInfo;
+                        if (DEBUG) {
+                            Objects.requireNonNull(info);
+                        }
                         updateGeneratedPreviewCategoriesLocked();
                     }
                 }
@@ -4699,12 +4728,18 @@
         @GuardedBy("AppWidgetServiceImpl.mLock")
         public void setPartialInfoLocked(AppWidgetProviderInfo info) {
             this.info = info;
+            if (DEBUG) {
+                Objects.requireNonNull(this.info);
+            }
             mInfoParsed = false;
         }
 
         @GuardedBy("AppWidgetServiceImpl.mLock")
         public void setInfoLocked(AppWidgetProviderInfo info) {
             this.info = info;
+            if (DEBUG) {
+                Objects.requireNonNull(this.info);
+            }
             mInfoParsed = true;
         }
 
diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
index 0e66fbc..71a1822 100644
--- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
+++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
@@ -23,6 +23,7 @@
 import android.os.Build;
 import android.util.Slog;
 
+import com.google.security.cryptauth.lib.securegcm.ukey2.AlertException;
 import com.google.security.cryptauth.lib.securegcm.ukey2.BadHandleException;
 import com.google.security.cryptauth.lib.securegcm.ukey2.CryptoException;
 import com.google.security.cryptauth.lib.securegcm.ukey2.D2DConnectionContextV1;
@@ -203,7 +204,8 @@
      *
      * This method must only be called from one of the two participants.
      */
-    public void establishSecureConnection() throws IOException, SecureChannelException {
+    public void establishSecureConnection() throws IOException,
+            SecureChannelException, HandshakeException {
         if (isSecured()) {
             Slog.d(TAG, "Channel is already secure.");
             return;
@@ -334,7 +336,7 @@
         }
     }
 
-    private void initiateHandshake() throws IOException, BadHandleException {
+    private void initiateHandshake() throws IOException, BadHandleException , HandshakeException {
         if (mConnectionContext != null) {
             Slog.d(TAG, "Ukey2 handshake is already completed.");
             return;
@@ -394,8 +396,8 @@
         }
     }
 
-    private void exchangeHandshake()
-            throws IOException, HandshakeException, BadHandleException, CryptoException {
+    private void exchangeHandshake() throws IOException, HandshakeException,
+            BadHandleException, CryptoException, AlertException {
         if (mConnectionContext != null) {
             Slog.d(TAG, "Ukey2 handshake is already completed.");
             return;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index d153c18..dc1155a 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -232,6 +232,7 @@
         "android.hardware.rebootescrow-V1-java",
         "android.hardware.power.stats-V2-java",
         "android.hidl.manager-V1.2-java",
+        "audio-permission-aidl-java",
         "cbor-java",
         "com.android.media.audio-aconfig-java",
         "icu4j_calendar_astronomer",
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 94bf813..9be0e1f 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3783,6 +3783,14 @@
         return fgsInfo.getLastFgsStartTime() + Math.max(0, timeLimit - fgsInfo.getTotalRuntime());
     }
 
+    private TimeLimitedFgsInfo getFgsTimeLimitedInfo(int uid, int fgsType) {
+        final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(uid);
+        if (fgsInfo != null) {
+            return fgsInfo.get(fgsType);
+        }
+        return null;
+    }
+
     private void maybeUpdateFgsTrackingLocked(ServiceRecord sr, int previousFgsType) {
         final int previouslyTimeLimitedType = getTimeLimitedFgsType(previousFgsType);
         if (previouslyTimeLimitedType == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE
@@ -3793,16 +3801,12 @@
 
         if (previouslyTimeLimitedType != ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE) {
             // FGS is switching types and the previous type was time-limited so update the runtime.
-            final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(sr.appInfo.uid);
-            if (fgsInfo != null) {
-                final TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(previouslyTimeLimitedType);
-                if (fgsTypeInfo != null) {
-                    // Update the total runtime for the previous time-limited fgs type.
-                    fgsTypeInfo.updateTotalRuntime();
-                    // TODO(b/330399444): handle the case where an app is running 2 services of the
-                    //  same time-limited type in parallel and stops one of them which leads to the
-                    //  second running one gaining additional runtime.
-                }
+            final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(
+                                                    sr.appInfo.uid, previouslyTimeLimitedType);
+            if (fgsTypeInfo != null) {
+                // Update the total runtime for the previous time-limited fgs type.
+                fgsTypeInfo.updateTotalRuntime(SystemClock.uptimeMillis());
+                fgsTypeInfo.decNumParallelServices();
             }
 
             if (!sr.isFgsTimeLimited()) {
@@ -3825,10 +3829,10 @@
         final int timeLimitedFgsType = getTimeLimitedFgsType(sr.foregroundServiceType);
         TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(timeLimitedFgsType);
         if (fgsTypeInfo == null) {
-            fgsTypeInfo = sr.createTimeLimitedFgsInfo(nowUptime);
+            fgsTypeInfo = sr.createTimeLimitedFgsInfo();
             fgsInfo.put(timeLimitedFgsType, fgsTypeInfo);
         }
-        fgsTypeInfo.setLastFgsStartTime(nowUptime);
+        fgsTypeInfo.noteFgsFgsStart(nowUptime);
 
         // We'll cancel the previous ANR timer and start a fresh one below.
         mFGSAnrTimer.cancel(sr);
@@ -3852,13 +3856,12 @@
             return; // if the current fgs type is not time-limited, return.
         }
 
-        final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(sr.appInfo.uid);
-        if (fgsInfo != null) {
-            final TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(timeLimitedType);
-            if (fgsTypeInfo != null) {
-                // Update the total runtime for the previous time-limited fgs type.
-                fgsTypeInfo.updateTotalRuntime();
-            }
+        final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(
+                                                sr.appInfo.uid, timeLimitedType);
+        if (fgsTypeInfo != null) {
+            // Update the total runtime for the previous time-limited fgs type.
+            fgsTypeInfo.updateTotalRuntime(SystemClock.uptimeMillis());
+            fgsTypeInfo.decNumParallelServices();
         }
         Slog.d(TAG_SERVICE, "Stop FGS timeout: " + sr);
         mFGSAnrTimer.cancel(sr);
@@ -3913,24 +3916,21 @@
             mFGSAnrTimer.accept(sr);
             traceInstant("FGS timed out: ", sr);
 
-            final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(sr.appInfo.uid);
-            if (fgsInfo != null) {
-                final TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(fgsType);
-                if (fgsTypeInfo != null) {
-                    // Update total runtime for the time-limited fgs type and mark it as timed out.
-                    fgsTypeInfo.updateTotalRuntime();
-                    fgsTypeInfo.setTimeLimitExceededAt(nowUptime);
+            final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(sr.appInfo.uid, fgsType);
+            if (fgsTypeInfo != null) {
+                // Update total runtime for the time-limited fgs type and mark it as timed out.
+                fgsTypeInfo.updateTotalRuntime(nowUptime);
+                fgsTypeInfo.setTimeLimitExceededAt(nowUptime);
 
-                    logFGSStateChangeLocked(sr,
-                            FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT,
-                            nowUptime > fgsTypeInfo.getLastFgsStartTime()
-                                    ? (int) (nowUptime - fgsTypeInfo.getLastFgsStartTime()) : 0,
-                            FGS_STOP_REASON_UNKNOWN,
-                            FGS_TYPE_POLICY_CHECK_UNKNOWN,
-                            FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
-                            false /* fgsRestrictionRecalculated */
-                    );
-                }
+                logFGSStateChangeLocked(sr,
+                        FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT,
+                        nowUptime > fgsTypeInfo.getFirstFgsStartUptime()
+                                ? (int) (nowUptime - fgsTypeInfo.getFirstFgsStartUptime()) : 0,
+                        FGS_STOP_REASON_UNKNOWN,
+                        FGS_TYPE_POLICY_CHECK_UNKNOWN,
+                        FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+                        false /* fgsRestrictionRecalculated */
+                );
             }
 
             try {
@@ -3949,6 +3949,16 @@
         if (fgsType == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE) {
             return; // no timed out FGS type was found (either it was stopped or it switched types)
         }
+
+        synchronized (mAm) {
+            final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(sr.appInfo.uid, fgsType);
+            if (fgsTypeInfo != null) {
+                // Runtime is already updated when the service times out - if the app didn't
+                // stop the service, decrement the number of parallel running services here.
+                fgsTypeInfo.decNumParallelServices();
+            }
+        }
+
         final String reason = "A foreground service of type "
                 + ServiceInfo.foregroundServiceTypeToLabel(fgsType)
                 + " did not stop within its timeout: " + sr.getComponentName();
@@ -3958,7 +3968,6 @@
             Slog.wtf(TAG, reason);
             return;
         }
-
         if (android.app.Flags.enableFgsTimeoutCrashBehavior()) {
             // Crash the app
             synchronized (mAm) {
@@ -4005,7 +4014,6 @@
 
     private void stopServiceAndUpdateAllowlistManagerLocked(ServiceRecord service) {
         maybeStopShortFgsTimeoutLocked(service);
-        maybeStopFgsTimeoutLocked(service);
         final ProcessServiceRecord psr = service.app.mServices;
         psr.stopService(service);
         psr.updateBoundClientUids();
@@ -6291,7 +6299,6 @@
             Slog.w(TAG_SERVICE, "Short FGS brought down without stopping: " + r);
             maybeStopShortFgsTimeoutLocked(r);
         }
-        maybeStopFgsTimeoutLocked(r);
 
         // Report to all of the connections that the service is no longer
         // available.
@@ -6416,7 +6423,6 @@
         final boolean exitingFg = r.isForeground;
         if (exitingFg) {
             maybeStopShortFgsTimeoutLocked(r);
-            maybeStopFgsTimeoutLocked(r);
             decActiveForegroundAppLocked(smap, r);
             synchronized (mAm.mProcessStats.mLock) {
                 ServiceState stracker = r.getTracker();
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 8eca4fc..2184340 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -690,10 +690,14 @@
         private long mTimeLimitExceededAt = Long.MIN_VALUE;
         @UptimeMillisLong
         private long mTotalRuntime = 0;
+        private int mNumParallelServices = 0;
 
-        TimeLimitedFgsInfo(@UptimeMillisLong long startTime) {
-            mFirstFgsStartUptime = startTime;
-            mFirstFgsStartRealtime = SystemClock.elapsedRealtime();
+        public void noteFgsFgsStart(@UptimeMillisLong long startTime) {
+            mNumParallelServices++;
+            if (mNumParallelServices == 1) {
+                mFirstFgsStartUptime = startTime;
+                mFirstFgsStartRealtime = SystemClock.elapsedRealtime();
+            }
             mLastFgsStartTime = startTime;
         }
 
@@ -707,17 +711,23 @@
             return mFirstFgsStartRealtime;
         }
 
-        public void setLastFgsStartTime(@UptimeMillisLong long startTime) {
-            mLastFgsStartTime = startTime;
-        }
-
         @UptimeMillisLong
         public long getLastFgsStartTime() {
             return mLastFgsStartTime;
         }
 
-        public void updateTotalRuntime() {
-            mTotalRuntime += SystemClock.uptimeMillis() - mLastFgsStartTime;
+        public void decNumParallelServices() {
+            if (mNumParallelServices > 0) {
+                mNumParallelServices--;
+            }
+            if (mNumParallelServices == 0) {
+                mLastFgsStartTime = 0;
+            }
+        }
+
+        public void updateTotalRuntime(@UptimeMillisLong long nowUptime) {
+            mTotalRuntime += nowUptime - mLastFgsStartTime;
+            mLastFgsStartTime = nowUptime;
         }
 
         @UptimeMillisLong
@@ -735,6 +745,7 @@
         }
 
         public void reset() {
+            mNumParallelServices = 0;
             mFirstFgsStartUptime = 0;
             mFirstFgsStartRealtime = 0;
             mLastFgsStartTime = 0;
@@ -1872,8 +1883,8 @@
     /**
      * Called when a time-limited FGS starts.
      */
-    public TimeLimitedFgsInfo createTimeLimitedFgsInfo(@UptimeMillisLong long nowUptime) {
-        return new TimeLimitedFgsInfo(nowUptime);
+    public TimeLimitedFgsInfo createTimeLimitedFgsInfo() {
+        return new TimeLimitedFgsInfo();
     }
 
     /**
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c310822..15c5c10 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4440,7 +4440,8 @@
                     || usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING) {
                 voiceActive = true;
             }
-            if (usage == AudioAttributes.USAGE_MEDIA || usage == AudioAttributes.USAGE_GAME) {
+            if (usage == AudioAttributes.USAGE_MEDIA || usage == AudioAttributes.USAGE_GAME
+                    || usage == AudioAttributes.USAGE_UNKNOWN) {
                 mediaActive = true;
             }
         }
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index e2c4b46..cae1695 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -347,9 +347,6 @@
     //------------------------------------------------------
     // routing monitoring
     synchronized void onRoutingUpdated() {
-        if (!mFeatureEnabled) {
-            return;
-        }
         switch (mState) {
             case STATE_UNINITIALIZED:
             case STATE_NOT_SUPPORTED:
@@ -393,7 +390,7 @@
             setDispatchAvailableState(false);
         }
 
-        boolean enabled = able && enabledAvailable.first;
+        boolean enabled = mFeatureEnabled && able && enabledAvailable.first;
         if (enabled) {
             loglogi("Enabling Spatial Audio since enabled for media device:"
                     + currentDevice);
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index d9c3ab8..30d12e6 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -1189,7 +1189,11 @@
         update();
     }
 
-    void switchMode(@AutomaticBrightnessMode int mode) {
+    /**
+     * Responsible for switching the AutomaticBrightnessMode of the associated display. Also takes
+     * care of resetting the short term model wherever required
+     */
+    public void switchMode(@AutomaticBrightnessMode int mode) {
         if (!mBrightnessMappingStrategyMap.contains(mode)) {
             return;
         }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 70a1014..875fd05 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -84,6 +84,7 @@
 import com.android.server.display.brightness.DisplayBrightnessController;
 import com.android.server.display.brightness.clamper.BrightnessClamperController;
 import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy2;
+import com.android.server.display.brightness.strategy.DisplayBrightnessStrategyConstants;
 import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
 import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
 import com.android.server.display.config.HysteresisLevels;
@@ -1333,12 +1334,6 @@
                 mDisplayStateController.shouldPerformScreenOffTransition());
         state = mPowerState.getScreenState();
 
-        // Switch to doze auto-brightness mode if needed
-        if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null
-                && !mAutomaticBrightnessController.isInIdleMode()) {
-            mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
-                    ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
-        }
 
         DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController
                 .updateBrightness(mPowerRequest, state);
@@ -1372,6 +1367,13 @@
         final boolean wasShortTermModelActive =
                 mAutomaticBrightnessStrategy.isShortTermModelActive();
         if (!mFlags.isRefactorDisplayPowerControllerEnabled()) {
+            // Switch to doze auto-brightness mode if needed
+            if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null
+                    && !mAutomaticBrightnessController.isInIdleMode()) {
+                mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
+                        ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
+            }
+
             mAutomaticBrightnessStrategy.setAutoBrightnessState(state,
                     mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(),
                     mBrightnessReasonTemp.getReason(), mPowerRequest.policy,
@@ -1440,45 +1442,52 @@
             brightnessState = clampScreenBrightness(brightnessState);
         }
 
-        // If there's an offload session, we need to set the initial doze brightness before
-        // the offload session starts controlling the brightness.
-        // During the transition DOZE_SUSPEND -> DOZE -> DOZE_SUSPEND, this brightness strategy
-        // will be selected again, meaning that no new brightness will be sent to the hardware and
-        // the display will stay at the brightness level set by the offload session.
-        if (Float.isNaN(brightnessState) && mFlags.isDisplayOffloadEnabled()
-                && Display.isDozeState(state) && mDisplayOffloadSession != null) {
-            if (mAutomaticBrightnessController != null
-                    && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
-                // Use the auto-brightness curve and the last observed lux
-                rawBrightnessState = mAutomaticBrightnessController
-                        .getAutomaticScreenBrightnessBasedOnLastUsedLux(
-                                mTempBrightnessEvent);
-            } else {
-                rawBrightnessState = getDozeBrightnessForOffload();
-                mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags()
-                        | BrightnessEvent.FLAG_DOZE_SCALE);
-            }
-
-            if (BrightnessUtils.isValidBrightnessValue(rawBrightnessState)) {
-                brightnessState = clampScreenBrightness(rawBrightnessState);
-                mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_INITIAL);
-
+        if (Display.isDozeState(state)) {
+            // If there's an offload session, we need to set the initial doze brightness before
+            // the offload session starts controlling the brightness.
+            // During the transition DOZE_SUSPEND -> DOZE -> DOZE_SUSPEND, this brightness strategy
+            // will be selected again, meaning that no new brightness will be sent to the hardware
+            // and the display will stay at the brightness level set by the offload session.
+            if ((Float.isNaN(brightnessState)
+                    || displayBrightnessState.getDisplayBrightnessStrategyName()
+                    .equals(DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME))
+                    && mFlags.isDisplayOffloadEnabled()
+                    && mDisplayOffloadSession != null) {
                 if (mAutomaticBrightnessController != null
                         && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
-                    // Keep the brightness in the setting so that we can use it after the screen
-                    // turns on, until a lux sample becomes available. We don't do this when
-                    // auto-brightness is disabled - in that situation we still want to use
-                    // the last brightness from when the screen was on.
-                    updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState;
+                    // Use the auto-brightness curve and the last observed lux
+                    rawBrightnessState = mAutomaticBrightnessController
+                            .getAutomaticScreenBrightnessBasedOnLastUsedLux(
+                                    mTempBrightnessEvent);
+                } else {
+                    rawBrightnessState = getDozeBrightnessForOffload();
+                    mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags()
+                            | BrightnessEvent.FLAG_DOZE_SCALE);
+                }
+
+                if (BrightnessUtils.isValidBrightnessValue(rawBrightnessState)) {
+                    brightnessState = clampScreenBrightness(rawBrightnessState);
+                    mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_INITIAL);
+
+                    if (mAutomaticBrightnessController != null
+                            && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
+                        // Keep the brightness in the setting so that we can use it after the screen
+                        // turns on, until a lux sample becomes available. We don't do this when
+                        // auto-brightness is disabled - in that situation we still want to use
+                        // the last brightness from when the screen was on.
+                        updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState;
+                    }
                 }
             }
-        }
 
-        // Use default brightness when dozing unless overridden.
-        if (Float.isNaN(brightnessState) && Display.isDozeState(state)) {
-            rawBrightnessState = mScreenBrightnessDozeConfig;
-            brightnessState = clampScreenBrightness(rawBrightnessState);
-            mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
+            // Use default brightness when dozing unless overridden.
+            if (Float.isNaN(brightnessState)
+                    || displayBrightnessState.getDisplayBrightnessStrategyName()
+                    .equals(DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME)) {
+                rawBrightnessState = mScreenBrightnessDozeConfig;
+                brightnessState = clampScreenBrightness(rawBrightnessState);
+                mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
+            }
         }
 
         if (!mFlags.isRefactorDisplayPowerControllerEnabled()) {
@@ -1502,7 +1511,7 @@
         }
 
         // Apply manual brightness.
-        if (Float.isNaN(brightnessState)) {
+        if (Float.isNaN(brightnessState) && !mFlags.isRefactorDisplayPowerControllerEnabled()) {
             rawBrightnessState = currentBrightnessSetting;
             brightnessState = clampScreenBrightness(rawBrightnessState);
             if (brightnessState != currentBrightnessSetting) {
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index 22a21a6..feec4e6 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -32,6 +32,7 @@
 import com.android.server.display.brightness.strategy.BoostBrightnessStrategy;
 import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
 import com.android.server.display.brightness.strategy.DozeBrightnessStrategy;
+import com.android.server.display.brightness.strategy.FallbackBrightnessStrategy;
 import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy;
 import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy;
 import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy;
@@ -85,6 +86,9 @@
     @Nullable
     private final AutoBrightnessFallbackStrategy mAutoBrightnessFallbackStrategy;
 
+    @Nullable
+    private final FallbackBrightnessStrategy mFallbackBrightnessStrategy;
+
     // A collective representation of all the strategies that the selector is aware of. This is
     // non null, but the strategies this is tracking can be null
     @NonNull
@@ -118,7 +122,8 @@
         mInvalidBrightnessStrategy = injector.getInvalidBrightnessStrategy();
         mAutomaticBrightnessStrategy1 =
                 (!mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) ? null
-                        : injector.getAutomaticBrightnessStrategy1(context, displayId);
+                        : injector.getAutomaticBrightnessStrategy1(context, displayId,
+                                mDisplayManagerFlags);
         mAutomaticBrightnessStrategy2 =
                 (mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) ? null
                         : injector.getAutomaticBrightnessStrategy2(context, displayId);
@@ -134,11 +139,14 @@
         } else {
             mOffloadBrightnessStrategy = null;
         }
+        mFallbackBrightnessStrategy = (mDisplayManagerFlags
+                .isRefactorDisplayPowerControllerEnabled())
+                ? injector.getFallbackBrightnessStrategy() : null;
         mDisplayBrightnessStrategies = new DisplayBrightnessStrategy[]{mInvalidBrightnessStrategy,
                 mScreenOffBrightnessStrategy, mDozeBrightnessStrategy, mFollowerBrightnessStrategy,
                 mBoostBrightnessStrategy, mOverrideBrightnessStrategy, mTemporaryBrightnessStrategy,
                 mAutomaticBrightnessStrategy1, mOffloadBrightnessStrategy,
-                mAutoBrightnessFallbackStrategy};
+                mAutoBrightnessFallbackStrategy, mFallbackBrightnessStrategy};
         mAllowAutoBrightnessWhileDozingConfig = context.getResources().getBoolean(
                 R.bool.config_allowAutoBrightnessWhileDozing);
         mOldBrightnessStrategyName = mInvalidBrightnessStrategy.getName();
@@ -179,6 +187,12 @@
             displayBrightnessStrategy = mOffloadBrightnessStrategy;
         } else if (isAutoBrightnessFallbackStrategyValid()) {
             displayBrightnessStrategy = mAutoBrightnessFallbackStrategy;
+        } else {
+            // This will become the ultimate fallback strategy once the flag has been fully rolled
+            // out
+            if (mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) {
+                displayBrightnessStrategy = mFallbackBrightnessStrategy;
+            }
         }
 
         if (mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) {
@@ -330,8 +344,8 @@
         }
 
         AutomaticBrightnessStrategy getAutomaticBrightnessStrategy1(Context context,
-                int displayId) {
-            return new AutomaticBrightnessStrategy(context, displayId);
+                int displayId, DisplayManagerFlags displayManagerFlags) {
+            return new AutomaticBrightnessStrategy(context, displayId, displayManagerFlags);
         }
 
         AutomaticBrightnessStrategy2 getAutomaticBrightnessStrategy2(Context context,
@@ -347,5 +361,9 @@
         AutoBrightnessFallbackStrategy getAutoBrightnessFallbackStrategy() {
             return new AutoBrightnessFallbackStrategy(/* injector= */ null);
         }
+
+        FallbackBrightnessStrategy getFallbackBrightnessStrategy() {
+            return new FallbackBrightnessStrategy();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 2305228..f809a49 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -17,6 +17,9 @@
 
 import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
 
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.display.BrightnessConfiguration;
@@ -33,6 +36,7 @@
 import com.android.server.display.brightness.BrightnessUtils;
 import com.android.server.display.brightness.StrategyExecutionRequest;
 import com.android.server.display.brightness.StrategySelectionNotifyRequest;
+import com.android.server.display.feature.DisplayManagerFlags;
 
 import java.io.PrintWriter;
 
@@ -98,19 +102,24 @@
 
     private Injector mInjector;
 
+    private DisplayManagerFlags mDisplayManagerFlags;
+
     @VisibleForTesting
-    AutomaticBrightnessStrategy(Context context, int displayId, Injector injector) {
+    AutomaticBrightnessStrategy(Context context, int displayId, Injector injector,
+            DisplayManagerFlags displayManagerFlags) {
         super(context, displayId);
         mContext = context;
         mDisplayId = displayId;
         mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
         mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        mDisplayManagerFlags  = displayManagerFlags;
         mInjector = (injector == null) ? new RealInjector() : injector;
     }
 
-    public AutomaticBrightnessStrategy(Context context, int displayId) {
-        this(context, displayId, null);
+    public AutomaticBrightnessStrategy(Context context, int displayId,
+            DisplayManagerFlags displayManagerFlags) {
+        this(context, displayId, null, displayManagerFlags);
     }
 
     /**
@@ -120,6 +129,7 @@
     public void setAutoBrightnessState(int targetDisplayState,
             boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy,
             float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) {
+        switchMode(targetDisplayState);
         final boolean autoBrightnessEnabledInDoze =
                 allowAutoBrightnessWhileDozingConfig && policy == POLICY_DOZE;
         mIsAutoBrightnessEnabled = shouldUseAutoBrightness()
@@ -479,6 +489,16 @@
         }
     }
 
+
+    private void switchMode(int state) {
+        if (mDisplayManagerFlags.areAutoBrightnessModesEnabled()
+                && mAutomaticBrightnessController != null
+                && !mAutomaticBrightnessController.isInIdleMode()) {
+            mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
+                    ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
+        }
+    }
+
     /**
      * Evaluates if there are any temporary auto-brightness adjustments which is not applied yet.
      * Temporary brightness adjustments happen when the user moves the brightness slider in the
diff --git a/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java b/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java
index 504683a..7b2f2b9 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java
@@ -18,4 +18,5 @@
 
 public class DisplayBrightnessStrategyConstants {
     static final String INVALID_BRIGHTNESS_STRATEGY_NAME = "InvalidBrightnessStrategy";
+    public static final String FALLBACK_BRIGHTNESS_STRATEGY_NAME = "FallbackBrightnessStrategy";
 }
diff --git a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
new file mode 100644
index 0000000..3463649a
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.strategy;
+
+import android.annotation.NonNull;
+
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.StrategyExecutionRequest;
+import com.android.server.display.brightness.StrategySelectionNotifyRequest;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages the brightness of the associated display when no other strategy qualifies for
+ * setting up the brightness state. This strategy is also being used for evaluating the
+ * display brightness state when we have a manually set brightness. This is a temporary state, and
+ * the logic for evaluating the manual brightness will be moved to a separate strategy
+ */
+public class FallbackBrightnessStrategy implements DisplayBrightnessStrategy{
+    @Override
+    public DisplayBrightnessState updateBrightness(
+            StrategyExecutionRequest strategyExecutionRequest) {
+        BrightnessReason brightnessReason = new BrightnessReason();
+        brightnessReason.setReason(BrightnessReason.REASON_MANUAL);
+        return new DisplayBrightnessState.Builder()
+                .setBrightness(strategyExecutionRequest.getCurrentScreenBrightness())
+                .setSdrBrightness(strategyExecutionRequest.getCurrentScreenBrightness())
+                .setBrightnessReason(brightnessReason)
+                .setDisplayBrightnessStrategyName(getName())
+                // The fallback brightness might change due to clamping. Make sure we tell the rest
+                // of the system by updating the setting
+                .setShouldUpdateScreenBrightnessSetting(true)
+                .build();
+    }
+
+    @NonNull
+    @Override
+    public String getName() {
+        return DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME;
+    }
+
+    @Override
+    public int getReason() {
+        return BrightnessReason.REASON_MANUAL;
+    }
+
+    @Override
+    public void dump(PrintWriter writer) {
+
+    }
+
+    @Override
+    public void strategySelectionPostProcessor(
+            StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
+
+    }
+}
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 816242d..c6aef7f 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -249,14 +249,14 @@
         mCurrentDream.mAppTask = appTask;
     }
 
-    void setDreamHasFocus(boolean hasFocus) {
+    void setDreamIsObscured(boolean isObscured) {
         if (mCurrentDream != null) {
-            mCurrentDream.mDreamHasFocus = hasFocus;
+            mCurrentDream.mDreamIsObscured = isObscured;
         }
     }
 
-    boolean dreamHasFocus() {
-        return mCurrentDream != null && mCurrentDream.mDreamHasFocus;
+    boolean dreamIsFrontmost() {
+        return mCurrentDream != null && mCurrentDream.dreamIsFrontmost();
     }
 
     /**
@@ -451,7 +451,7 @@
         private String mStopReason;
         private long mDreamStartTime;
         public boolean mWakingGently;
-        public boolean mDreamHasFocus;
+        private boolean mDreamIsObscured;
 
         private final Runnable mStopPreviousDreamsIfNeeded = this::stopPreviousDreamsIfNeeded;
         private final Runnable mReleaseWakeLockIfNeeded = this::releaseWakeLockIfNeeded;
@@ -549,5 +549,9 @@
                 mHandler.removeCallbacks(mReleaseWakeLockIfNeeded);
             }
         }
+
+        boolean dreamIsFrontmost() {
+            return !mDreamIsObscured;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 2def5ae..18a9986 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -20,7 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.service.dreams.Flags.dreamTracksFocus;
+import static android.service.dreams.Flags.dreamHandlesBeingObscured;
 
 import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID;
 
@@ -428,7 +428,7 @@
             // Can't start dreaming if we are already dreaming and the dream has focus. If we are
             // dreaming but the dream does not have focus, then the dream can be brought to the
             // front so it does have focus.
-            if (isScreenOn && isDreamingInternal() && dreamHasFocus()) {
+            if (isScreenOn && isDreamingInternal() && dreamIsFrontmost()) {
                 return false;
             }
 
@@ -463,9 +463,10 @@
         }
     }
 
-    private boolean dreamHasFocus() {
-        // Dreams always had focus before they were able to track it.
-        return !dreamTracksFocus() || mController.dreamHasFocus();
+    private boolean dreamIsFrontmost() {
+        // Dreams were always considered frontmost before they began tracking whether they are
+        // obscured.
+        return !dreamHandlesBeingObscured() || mController.dreamIsFrontmost();
     }
 
     protected void requestStartDreamFromShell() {
@@ -473,7 +474,7 @@
     }
 
     private void requestDreamInternal() {
-        if (isDreamingInternal() && !dreamHasFocus() && mController.bringDreamToFront()) {
+        if (isDreamingInternal() && !dreamIsFrontmost() && mController.bringDreamToFront()) {
             return;
         }
 
@@ -1159,10 +1160,16 @@
         }
 
         @Override
-        public void onDreamFocusChanged(boolean hasFocus) {
+        public void setDreamIsObscured(boolean isObscured) {
+            if (!dreamHandlesBeingObscured()) {
+                return;
+            }
+
+            checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+
             final long ident = Binder.clearCallingIdentity();
             try {
-                mController.setDreamHasFocus(hasFocus);
+                mHandler.post(() -> mController.setDreamIsObscured(isObscured));
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index b47631c3..d32a5ed 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -218,4 +218,13 @@
      * display, external peripherals, fingerprint sensor, etc.
      */
     public abstract void notifyUserActivity();
+
+    /**
+     * Get the device ID of the {@link InputDevice} that used most recently.
+     *
+     * @return the last used input device ID, or
+     *     {@link android.os.IInputConstants#INVALID_INPUT_DEVICE_ID} if no device has been used
+     *     since boot.
+     */
+    public abstract int getLastUsedInputDeviceId();
 }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 8317991..8685d2c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -3204,6 +3204,11 @@
         public void setStylusButtonMotionEventsEnabled(boolean enabled) {
             mNative.setStylusButtonMotionEventsEnabled(enabled);
         }
+
+        @Override
+        public int getLastUsedInputDeviceId() {
+            return mNative.getLastUsedInputDeviceId();
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index f742360..0208a32 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -271,6 +271,15 @@
 
     void setInputMethodConnectionIsActive(boolean isActive);
 
+    /**
+     * Get the device ID of the InputDevice that used most recently.
+     *
+     * @return the last used input device ID, or
+     *     {@link android.os.IInputConstants#INVALID_INPUT_DEVICE_ID} if no device has been used
+     *     since boot.
+     */
+    int getLastUsedInputDeviceId();
+
     /** The native implementation of InputManagerService methods. */
     class NativeImpl implements NativeInputManagerService {
         /** Pointer to native input manager service object, used by native code. */
@@ -544,5 +553,8 @@
 
         @Override
         public native void setInputMethodConnectionIsActive(boolean isActive);
+
+        @Override
+        public native int getLastUsedInputDeviceId();
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 67df992..691145c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -533,21 +533,6 @@
     EditorInfo mCurEditorInfo;
 
     /**
-     * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently
-     * connected to or in the process of connecting to.
-     *
-     * <p>This can be {@code null} when no input method is connected.</p>
-     *
-     * @see #getSelectedMethodIdLocked()
-     */
-    @GuardedBy("ImfLock.class")
-    @Nullable
-    private String getCurIdLocked() {
-        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
-        return userData.mBindingController.getCurId();
-    }
-
-    /**
      * The current subtype of the current input method.
      */
     @MultiUserUnawareField
@@ -577,16 +562,6 @@
     boolean mInFullscreenMode;
 
     /**
-     * The Intent used to connect to the current input method.
-     */
-    @GuardedBy("ImfLock.class")
-    @Nullable
-    private Intent getCurIntentLocked() {
-        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
-        return userData.mBindingController.getCurIntent();
-    }
-
-    /**
      * The token we have made for the currently active input method, to
      * identify it in the future.
      */
@@ -632,15 +607,6 @@
     }
 
     /**
-     * If not {@link Process#INVALID_UID}, then the UID of {@link #getCurIntentLocked()}.
-     */
-    @GuardedBy("ImfLock.class")
-    private int getCurMethodUidLocked() {
-        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
-        return userData.mBindingController.getCurMethodUid();
-    }
-
-    /**
      * Have we called mCurMethod.bindInput()?
      */
     @MultiUserUnawareField
@@ -2011,8 +1977,8 @@
         final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
         final StartInputInfo info = new StartInputInfo(mCurrentUserId,
                 getCurTokenLocked(),
-                mCurTokenDisplayId, getCurIdLocked(), startInputReason, restarting,
-                UserHandle.getUserId(mCurClient.mUid),
+                mCurTokenDisplayId, userData.mBindingController.getCurId(), startInputReason,
+                restarting, UserHandle.getUserId(mCurClient.mUid),
                 mCurClient.mSelfReportedDisplayId, mImeBindingState.mFocusedWindow, mCurEditorInfo,
                 mImeBindingState.mFocusedWindowSoftInputMode,
                 userData.mBindingController.getSequenceNumber());
@@ -2026,8 +1992,8 @@
         // INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case.
         if (mCurrentUserId == UserHandle.getUserId(
                 mCurClient.mUid)) {
-            mPackageManagerInternal.grantImplicitAccess(mCurrentUserId,
-                    null /* intent */, UserHandle.getAppId(getCurMethodUidLocked()),
+            mPackageManagerInternal.grantImplicitAccess(mCurrentUserId, null /* intent */,
+                    UserHandle.getAppId(userData.mBindingController.getCurMethodUid()),
                     mCurClient.mUid, true /* direct */);
         }
 
@@ -2048,7 +2014,7 @@
                     null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT);
         }
 
-        final var curId = getCurIdLocked();
+        final var curId = userData.mBindingController.getCurId();
         final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(mCurrentUserId)
                 .getMethodMap().get(curId);
         final boolean suppressesSpellChecker =
@@ -2337,7 +2303,8 @@
                 requestClientSessionForAccessibilityLocked(cs);
                 return new InputBindResult(
                         InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
-                        null, null, null, getCurIdLocked(),
+                        null, null, null,
+                        userData.mBindingController.getCurId(),
                         userData.mBindingController.getSequenceNumber(), false);
             } else {
                 final long lastBindTime = userData.mBindingController.getLastBindTime();
@@ -2352,7 +2319,8 @@
                     // to see if we can get back in touch with the service.
                     return new InputBindResult(
                             InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
-                            null, null, null, getCurIdLocked(),
+                            null, null, null,
+                            userData.mBindingController.getCurId(),
                             userData.mBindingController.getSequenceNumber(), false);
                 } else {
                     EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
@@ -2707,7 +2675,8 @@
         // When the IME switcher dialog is shown, the IME switcher button should be hidden.
         if (mMenuController.getSwitchingDialogLocked() != null) return false;
         // When we are switching IMEs, the IME switcher button should be hidden.
-        if (!Objects.equals(getCurIdLocked(), getSelectedMethodIdLocked())) {
+        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+        if (!Objects.equals(userData.mBindingController.getCurId(), getSelectedMethodIdLocked())) {
             return false;
         }
         if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded()
@@ -2869,8 +2838,10 @@
             } else {
                 vis &= ~InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
             }
+            final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+            final var curId = userData.mBindingController.getCurId();
             if (mMenuController.getSwitchingDialogLocked() != null
-                    || !Objects.equals(getCurIdLocked(), getSelectedMethodIdLocked())) {
+                    || !Objects.equals(curId, getSelectedMethodIdLocked())) {
                 // When the IME switcher dialog is shown, or we are switching IMEs,
                 // the back button should be in the default state (as if the IME is not shown).
                 backDisposition = InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING;
@@ -3831,10 +3802,10 @@
         if (mCurrentUserId != UserHandle.getUserId(uid)) {
             return false;
         }
-        if (getCurIntentLocked() != null && InputMethodUtils.checkIfPackageBelongsToUid(
-                mPackageManagerInternal,
-                uid,
-                getCurIntentLocked().getComponent().getPackageName())) {
+        final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+        final var curIntent = userData.mBindingController.getCurIntent();
+        if (curIntent != null && InputMethodUtils.checkIfPackageBelongsToUid(
+                mPackageManagerInternal, uid, curIntent.getComponent().getPackageName())) {
             return true;
         }
         return false;
@@ -4438,7 +4409,7 @@
             if (mCurEditorInfo != null) {
                 mCurEditorInfo.dumpDebug(proto, CUR_ATTRIBUTE);
             }
-            proto.write(CUR_ID, getCurIdLocked());
+            proto.write(CUR_ID, userData.mBindingController.getCurId());
             mVisibilityStateComputer.dumpDebug(proto, fieldId);
             proto.write(IN_FULLSCREEN_MODE, mInFullscreenMode);
             proto.write(CUR_TOKEN, Objects.toString(getCurTokenLocked()));
@@ -4895,7 +4866,10 @@
             if (mCurClient == null || mCurClient.mClient == null) {
                 return;
             }
-            if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(getCurMethodUidLocked())) {
+            // TODO(b/325515685): user data must be retrieved by a userId parameter
+            final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+            if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(
+                    userData.mBindingController.getCurMethodUid())) {
                 // Handle IME visibility when interactive changed before finishing the input to
                 // ensure we preserve the last state as possible.
                 final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged(
@@ -5648,7 +5622,7 @@
                     final InputBindResult res = new InputBindResult(
                             InputBindResult.ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION,
                             imeSession, accessibilityInputMethodSessions, /* channel= */ null,
-                            getCurIdLocked(),
+                            userData.mBindingController.getCurId(),
                             userData.mBindingController.getSequenceNumber(),
                             /* isInputMethodSuppressingSpellChecker= */ false);
                     mCurClient.mClient.onBindAccessibilityService(res, accessibilityConnectionId);
@@ -5901,7 +5875,7 @@
             p.println("  mFocusedWindowPerceptible=" + mFocusedWindowPerceptible);
             mImeBindingState.dump(/* prefix= */ "  ", p);
 
-            p.println("  mCurId=" + getCurIdLocked()
+            p.println("  mCurId=" + userData.mBindingController.getCurId()
                     + " mHaveConnection=" + userData.mBindingController.hasMainConnection()
                     + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound="
                     + userData.mBindingController.isVisibleBound());
@@ -5920,7 +5894,7 @@
             p.println("  mCurToken=" + getCurTokenLocked());
             p.println("  mCurTokenDisplayId=" + mCurTokenDisplayId);
             p.println("  mCurHostInputToken=" + mAutofillController.getCurHostInputToken());
-            p.println("  mCurIntent=" + getCurIntentLocked());
+            p.println("  mCurIntent=" + userData.mBindingController.getCurIntent());
             method = getCurMethodLocked();
             p.println("  mCurMethod=" + getCurMethodLocked());
             p.println("  mEnabledSession=" + mEnabledSession);
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index 99401a1..235e3cd 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -16,6 +16,10 @@
 
 package com.android.server.ondeviceintelligence;
 
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BUNDLE_KEY;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BUNDLE_KEY;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY;
+
 import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeInferenceParams;
 import static com.android.server.ondeviceintelligence.BundleUtil.validatePfdReadOnly;
 import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeStateParams;
@@ -41,6 +45,7 @@
 import android.app.ondeviceintelligence.OnDeviceIntelligenceException;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
@@ -105,12 +110,20 @@
     /** Handler message to {@link #resetTemporaryServices()} */
     private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
 
+    /** Handler message to clean up temporary broadcast keys. */
+    private static final int MSG_RESET_BROADCAST_KEYS = 1;
+
     /** Default value in absence of {@link DeviceConfig} override. */
     private static final boolean DEFAULT_SERVICE_ENABLED = true;
     private static final String NAMESPACE_ON_DEVICE_INTELLIGENCE = "ondeviceintelligence";
 
+    private static final String SYSTEM_PACKAGE = "android";
+
+
     private final Executor resourceClosingExecutor = Executors.newCachedThreadPool();
     private final Executor callbackExecutor = Executors.newCachedThreadPool();
+    private final Executor broadcastExecutor = Executors.newCachedThreadPool();
+
 
     private final Context mContext;
     protected final Object mLock = new Object();
@@ -123,10 +136,14 @@
     @GuardedBy("mLock")
     private String[] mTemporaryServiceNames;
 
+    @GuardedBy("mLock")
+    private String[] mTemporaryBroadcastKeys;
+    @GuardedBy("mLock")
+    private String mBroadcastPackageName;
+
     /**
      * Handler used to reset the temporary service names.
      */
-    @GuardedBy("mLock")
     private Handler mTemporaryHandler;
 
     public OnDeviceIntelligenceManagerService(Context context) {
@@ -482,6 +499,8 @@
                                     ensureRemoteIntelligenceServiceInitialized();
                                     mRemoteOnDeviceIntelligenceService.run(
                                             IOnDeviceIntelligenceService::notifyInferenceServiceConnected);
+                                    broadcastExecutor.execute(
+                                            () -> registerModelLoadingBroadcasts(service));
                                     service.registerRemoteStorageService(
                                             getIRemoteStorageService());
                                 } catch (RemoteException ex) {
@@ -493,6 +512,56 @@
         }
     }
 
+    private void registerModelLoadingBroadcasts(IOnDeviceSandboxedInferenceService service) {
+        String[] modelBroadcastKeys;
+        try {
+            modelBroadcastKeys = getBroadcastKeys();
+        } catch (Resources.NotFoundException e) {
+            Slog.d(TAG, "Skipping model broadcasts as broadcast intents configured.");
+            return;
+        }
+
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY, true);
+        try {
+            service.updateProcessingState(bundle, new IProcessingUpdateStatusCallback.Stub() {
+                @Override
+                public void onSuccess(PersistableBundle statusParams) {
+                    Binder.clearCallingIdentity();
+                    synchronized (mLock) {
+                        if (statusParams.containsKey(MODEL_LOADED_BUNDLE_KEY)) {
+                            String modelLoadedBroadcastKey = modelBroadcastKeys[0];
+                            if (modelLoadedBroadcastKey != null
+                                    && !modelLoadedBroadcastKey.isEmpty()) {
+                                final Intent intent = new Intent(modelLoadedBroadcastKey);
+                                intent.setPackage(mBroadcastPackageName);
+                                mContext.sendBroadcast(intent,
+                                        Manifest.permission.USE_ON_DEVICE_INTELLIGENCE);
+                            }
+                        } else if (statusParams.containsKey(MODEL_UNLOADED_BUNDLE_KEY)) {
+                            String modelUnloadedBroadcastKey = modelBroadcastKeys[1];
+                            if (modelUnloadedBroadcastKey != null
+                                    && !modelUnloadedBroadcastKey.isEmpty()) {
+                                final Intent intent = new Intent(modelUnloadedBroadcastKey);
+                                intent.setPackage(mBroadcastPackageName);
+                                mContext.sendBroadcast(intent,
+                                        Manifest.permission.USE_ON_DEVICE_INTELLIGENCE);
+                            }
+                        }
+                    }
+                }
+
+                @Override
+                public void onFailure(int errorCode, String errorMessage) {
+                    Slog.e(TAG, "Failed to register model loading callback with status code",
+                            new OnDeviceIntelligenceException(errorCode, errorMessage));
+                }
+            });
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to register model loading callback with status code", e);
+        }
+    }
+
     @NonNull
     private IRemoteStorageService.Stub getIRemoteStorageService() {
         return new IRemoteStorageService.Stub() {
@@ -629,6 +698,20 @@
                         R.string.config_defaultOnDeviceSandboxedInferenceService)};
     }
 
+    protected String[] getBroadcastKeys() throws Resources.NotFoundException {
+        // TODO 329240495 : Consider a small class with explicit field names for the two services
+        synchronized (mLock) {
+            if (mTemporaryBroadcastKeys != null && mTemporaryBroadcastKeys.length == 2) {
+                return mTemporaryBroadcastKeys;
+            }
+        }
+
+        return new String[]{mContext.getResources().getString(
+                R.string.config_onDeviceIntelligenceModelLoadedBroadcastKey),
+                mContext.getResources().getString(
+                        R.string.config_onDeviceIntelligenceModelUnloadedBroadcastKey)};
+    }
+
     @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
     public void setTemporaryServices(@NonNull String[] componentNames, int durationMs) {
         Objects.requireNonNull(componentNames);
@@ -645,25 +728,26 @@
                 mRemoteOnDeviceIntelligenceService.unbind();
                 mRemoteOnDeviceIntelligenceService = null;
             }
-            if (mTemporaryHandler == null) {
-                mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
-                    @Override
-                    public void handleMessage(Message msg) {
-                        if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
-                            synchronized (mLock) {
-                                resetTemporaryServices();
-                            }
-                        } else {
-                            Slog.wtf(TAG, "invalid handler msg: " + msg);
-                        }
-                    }
-                };
-            } else {
-                mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
-            }
 
             if (durationMs != -1) {
-                mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
+                getTemporaryHandler().sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE,
+                        durationMs);
+            }
+        }
+    }
+
+    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+    public void setModelBroadcastKeys(@NonNull String[] broadcastKeys, String receiverPackageName,
+            int durationMs) {
+        Objects.requireNonNull(broadcastKeys);
+        enforceShellOnly(Binder.getCallingUid(), "setModelBroadcastKeys");
+        mContext.enforceCallingPermission(
+                Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+        synchronized (mLock) {
+            mTemporaryBroadcastKeys = broadcastKeys;
+            mBroadcastPackageName = receiverPackageName;
+            if (durationMs != -1) {
+                getTemporaryHandler().sendEmptyMessageDelayed(MSG_RESET_BROADCAST_KEYS, durationMs);
             }
         }
     }
@@ -751,4 +835,28 @@
             }
         }
     }
+
+    private synchronized Handler getTemporaryHandler() {
+        if (mTemporaryHandler == null) {
+            mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
+                @Override
+                public void handleMessage(Message msg) {
+                    if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
+                        synchronized (mLock) {
+                            resetTemporaryServices();
+                        }
+                    } else if (msg.what == MSG_RESET_BROADCAST_KEYS) {
+                        synchronized (mLock) {
+                            mTemporaryBroadcastKeys = null;
+                            mBroadcastPackageName = SYSTEM_PACKAGE;
+                        }
+                    } else {
+                        Slog.wtf(TAG, "invalid handler msg: " + msg);
+                    }
+                }
+            };
+        }
+
+        return mTemporaryHandler;
+    }
 }
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
index a76d8a3..5744b5c 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
@@ -43,6 +43,8 @@
                 return setTemporaryServices();
             case "get-services":
                 return getConfiguredServices();
+            case "set-model-broadcasts":
+                return setBroadcastKeys();
             default:
                 return handleDefaultCommands(cmd);
         }
@@ -62,12 +64,18 @@
         pw.println("    To reset, call without any arguments.");
 
         pw.println("  get-services To get the names of services that are currently being used.");
+        pw.println(
+                "  set-model-broadcasts [ModelLoadedBroadcastKey] [ModelUnloadedBroadcastKey] "
+                        + "[ReceiverPackageName] "
+                        + "[DURATION] To set the names of broadcast intent keys that are to be "
+                        + "emitted for cts tests.");
     }
 
     private int setTemporaryServices() {
         final PrintWriter out = getOutPrintWriter();
         final String intelligenceServiceName = getNextArg();
         final String inferenceServiceName = getNextArg();
+
         if (getRemainingArgsCount() == 0 && intelligenceServiceName == null
                 && inferenceServiceName == null) {
             mService.resetTemporaryServices();
@@ -79,7 +87,8 @@
         Objects.requireNonNull(inferenceServiceName);
         final int duration = Integer.parseInt(getNextArgRequired());
         mService.setTemporaryServices(
-                new String[]{intelligenceServiceName, inferenceServiceName}, duration);
+                new String[]{intelligenceServiceName, inferenceServiceName},
+                duration);
         out.println("OnDeviceIntelligenceService temporarily set to " + intelligenceServiceName
                 + " \n and \n OnDeviceTrustedInferenceService set to " + inferenceServiceName
                 + " for " + duration + "ms");
@@ -93,4 +102,22 @@
                 + " \n and \n OnDeviceTrustedInferenceService set to : " + services[1]);
         return 0;
     }
+
+    private int setBroadcastKeys() {
+        final PrintWriter out = getOutPrintWriter();
+        final String modelLoadedKey = getNextArgRequired();
+        final String modelUnloadedKey = getNextArgRequired();
+        final String receiverPackageName = getNextArg();
+
+        final int duration = Integer.parseInt(getNextArgRequired());
+        mService.setModelBroadcastKeys(
+                new String[]{modelLoadedKey, modelUnloadedKey}, receiverPackageName, duration);
+        out.println("OnDeviceIntelligence Model Loading broadcast keys temporarily set to "
+                + modelLoadedKey
+                + " \n and \n OnDeviceTrustedInferenceService set to " + modelUnloadedKey
+                + "\n and Package name set to : " + receiverPackageName
+                + " for " + duration + "ms");
+        return 0;
+    }
+
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index 9ba88aa..fe774aa 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -504,9 +504,12 @@
         } else {
             storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
         }
-        List<String> deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
-                UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
-                true /* onlyCoreApps */);
+        final List<String> deferPackages;
+        synchronized (mPm.mInstallLock) {
+           deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
+                    UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
+                    true /* onlyCoreApps */);
+        }
         Future<?> prepareAppDataFuture = SystemServerInitThreadPool.submit(() -> {
             TimingsTraceLog traceLog = new TimingsTraceLog("SystemServerTimingAsync",
                     Trace.TRACE_TAG_PACKAGE_MANAGER);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 19a0ba7..908b47d 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -985,13 +985,13 @@
     }
 
     void installPackagesTraced(List<InstallRequest> requests) {
-        synchronized (mPm.mInstallLock) {
-            try {
-                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
-                installPackagesLI(requests);
-            } finally {
-                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-            }
+        mPm.mInstallLock.lock();
+        try {
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
+            installPackagesLI(requests);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+            mPm.mInstallLock.unlock();
         }
     }
 
@@ -2590,22 +2590,30 @@
             final boolean performDexopt = DexOptHelper.shouldPerformDexopt(installRequest,
                     dexoptOptions, mContext);
             if (performDexopt) {
-                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+                // dexopt can take long, and ArtService doesn't require installd, so we release
+                // the lock here and re-acquire the lock after dexopt is finished.
+                mPm.mInstallLock.unlock();
+                try {
+                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
 
-                // This mirrors logic from commitReconciledScanResultLocked, where the library files
-                // needed for dexopt are assigned.
-                PackageSetting realPkgSetting = installRequest.getRealPackageSetting();
+                    // This mirrors logic from commitReconciledScanResultLocked, where the library
+                    // files needed for dexopt are assigned.
+                    PackageSetting realPkgSetting = installRequest.getRealPackageSetting();
 
-                // Unfortunately, the updated system app flag is only tracked on this PackageSetting
-                boolean isUpdatedSystemApp =
-                        installRequest.getScannedPackageSetting().isUpdatedSystemApp();
+                    // Unfortunately, the updated system app flag is only tracked on this
+                    // PackageSetting
+                    boolean isUpdatedSystemApp =
+                            installRequest.getScannedPackageSetting().isUpdatedSystemApp();
 
-                realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
+                    realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
 
-                DexoptResult dexOptResult =
-                        DexOptHelper.dexoptPackageUsingArtService(installRequest, dexoptOptions);
-                installRequest.onDexoptFinished(dexOptResult);
-                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                    DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService(
+                            installRequest, dexoptOptions);
+                    installRequest.onDexoptFinished(dexOptResult);
+                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                } finally {
+                    mPm.mInstallLock.lock();
+                }
             }
         }
         PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ae485ed..121cf3f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -626,7 +626,7 @@
     // Lock for state used when installing and doing other long running
     // operations.  Methods that must be called with this lock held have
     // the suffix "LI".
-    final Object mInstallLock;
+    final PackageManagerTracedLock mInstallLock;
 
     // ----------------------------------------------------------------
 
@@ -1692,8 +1692,8 @@
         final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
                 Trace.TRACE_TAG_PACKAGE_MANAGER);
         t.traceBegin("create package manager");
-        final PackageManagerTracedLock lock = new PackageManagerTracedLock();
-        final Object installLock = new Object();
+        final PackageManagerTracedLock lock = new PackageManagerTracedLock("mLock");
+        final PackageManagerTracedLock installLock = new PackageManagerTracedLock("mInstallLock");
 
         HandlerThread backgroundThread = new ServiceThread("PackageManagerBg",
                 Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 83f3b16..ae2eaeb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -86,7 +86,7 @@
     private final Context mContext;
     private final PackageManagerTracedLock mLock;
     private final Installer mInstaller;
-    private final Object mInstallLock;
+    private final PackageManagerTracedLock mInstallLock;
     private final Handler mBackgroundHandler;
     private final Executor mBackgroundExecutor;
     private final List<ScanPartition> mSystemPartitions;
@@ -144,7 +144,7 @@
     private final Singleton<PackageMonitorCallbackHelper> mPackageMonitorCallbackHelper;
 
     PackageManagerServiceInjector(Context context, PackageManagerTracedLock lock,
-            Installer installer, Object installLock, PackageAbiHelper abiHelper,
+            Installer installer, PackageManagerTracedLock installLock, PackageAbiHelper abiHelper,
             Handler backgroundHandler,
             List<ScanPartition> systemPartitions,
             Producer<ComponentResolver> componentResolverProducer,
@@ -254,7 +254,7 @@
         return mAbiHelper;
     }
 
-    public Object getInstallLock() {
+    public PackageManagerTracedLock getInstallLock() {
         return mInstallLock;
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerTracedLock.java b/services/core/java/com/android/server/pm/PackageManagerTracedLock.java
index 75e1803f..303b8b9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerTracedLock.java
+++ b/services/core/java/com/android/server/pm/PackageManagerTracedLock.java
@@ -16,6 +16,9 @@
 
 package com.android.server.pm;
 
+import android.annotation.Nullable;
+import android.util.Slog;
+
 import java.util.concurrent.locks.ReentrantLock;
 
 /**
@@ -23,4 +26,31 @@
  * injection, similar to {@link ActivityManagerGlobalLock}.
  */
 public class PackageManagerTracedLock extends ReentrantLock {
+    private static final String TAG = "PackageManagerTracedLock";
+    private static final boolean DEBUG = false;
+    @Nullable private final String mLockName;
+
+    public PackageManagerTracedLock(@Nullable String lockName) {
+        mLockName = lockName;
+    }
+
+    public PackageManagerTracedLock() {
+        this(null);
+    }
+
+    @Override
+    public void lock() {
+        super.lock();
+        if (DEBUG && mLockName != null) {
+            Slog.i(TAG, "locked " + mLockName);
+        }
+    }
+
+    @Override
+    public void unlock() {
+        super.unlock();
+        if (DEBUG && mLockName != null) {
+            Slog.i(TAG, "unlocked " + mLockName);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java
index 1d41401..ef32485 100644
--- a/services/core/java/com/android/server/pm/UserDataPreparer.java
+++ b/services/core/java/com/android/server/pm/UserDataPreparer.java
@@ -52,11 +52,11 @@
     private static final String TAG = "UserDataPreparer";
     private static final String XATTR_SERIAL = "user.serial";
 
-    private final Object mInstallLock;
+    private final PackageManagerTracedLock mInstallLock;
     private final Context mContext;
     private final Installer mInstaller;
 
-    UserDataPreparer(Installer installer, Object installLock, Context context) {
+    UserDataPreparer(Installer installer, PackageManagerTracedLock installLock, Context context) {
         mInstallLock = installLock;
         mContext = context;
         mInstaller = installer;
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index c9703d8..0e4f033 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -1732,7 +1732,10 @@
             // The activity was detached from hierarchy.
             return;
         }
-        activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
+
+        if (activity.mDisplayContent.isFixedRotationLaunchingApp(activity)) {
+            activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
+        }
 
         // Restore the launch-behind state.
         activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token);
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 9e16b8a..57827c5 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -989,6 +989,10 @@
         }
     }
 
+    boolean isLetterboxEducationEnabled() {
+        return mLetterboxConfiguration.getIsEducationEnabled();
+    }
+
     /**
      * Whether we use split screen aspect ratio for the activity when camera compat treatment
      * is active because the corresponding config is enabled and activity supports resizing.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8bd7b5f..8defec3 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3448,6 +3448,8 @@
         // Whether the direct top activity is eligible for letterbox education.
         appCompatTaskInfo.topActivityEligibleForLetterboxEducation = isTopActivityResumed
                 && top.isEligibleForLetterboxEducation();
+        appCompatTaskInfo.isLetterboxEducationEnabled = top != null
+                && top.mLetterboxUiController.isLetterboxEducationEnabled();
         // Whether the direct top activity requested showing camera compat control.
         appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState = isTopActivityResumed
                 ? top.getCameraCompatControlState()
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index a01c123..74ca9ad 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -423,7 +423,7 @@
         std::set<int32_t> disabledInputDevices{};
 
         // Associated Pointer controller display.
-        ui::LogicalDisplayId pointerDisplayId{ui::ADISPLAY_ID_DEFAULT};
+        ui::LogicalDisplayId pointerDisplayId{ui::LogicalDisplayId::DEFAULT};
 
         // True if stylus button reporting through motion events is enabled.
         bool stylusButtonMotionEventsEnabled{true};
@@ -1886,7 +1886,7 @@
                                         jstring nameObj, jint pid) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
 
-    if (displayId == ui::ADISPLAY_ID_NONE.val()) {
+    if (ui::LogicalDisplayId{displayId} == ui::LogicalDisplayId::INVALID) {
         std::string message = "InputChannel used as a monitor must be associated with a display";
         jniThrowRuntimeException(env, message.c_str());
         return nullptr;
@@ -2727,6 +2727,11 @@
     im->setInputMethodConnectionIsActive(isActive);
 }
 
+static jint nativeGetLastUsedInputDeviceId(JNIEnv* env, jobject nativeImplObj) {
+    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+    return static_cast<jint>(im->getInputManager()->getReader().getLastUsedInputDeviceId());
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gInputManagerMethods[] = {
@@ -2835,6 +2840,7 @@
         {"setAccessibilityStickyKeysEnabled", "(Z)V",
          (void*)nativeSetAccessibilityStickyKeysEnabled},
         {"setInputMethodConnectionIsActive", "(Z)V", (void*)nativeSetInputMethodConnectionIsActive},
+        {"getLastUsedInputDeviceId", "()I", (void*)nativeGetLastUsedInputDeviceId},
 };
 
 #define FIND_CLASS(var, className) \
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f75803f..2b93d21 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -339,6 +339,7 @@
 import android.app.admin.ManagedSubscriptionsPolicy;
 import android.app.admin.NetworkEvent;
 import android.app.admin.PackagePolicy;
+import android.app.admin.PackageSetPolicyValue;
 import android.app.admin.ParcelableGranteeMap;
 import android.app.admin.ParcelableResource;
 import android.app.admin.PasswordMetrics;
@@ -349,7 +350,6 @@
 import android.app.admin.PreferentialNetworkServiceConfig;
 import android.app.admin.SecurityLog;
 import android.app.admin.SecurityLog.SecurityEvent;
-import android.app.admin.PackageSetPolicyValue;
 import android.app.admin.StartInstallingUpdateCallback;
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
@@ -2718,6 +2718,7 @@
                     mDevicePolicyEngine.getResolvedPolicy(
                             PolicyDefinition.SECURITY_LOGGING, UserHandle.USER_ALL));
             setLoggingConfiguration(securityLoggingEnabled, auditLoggingEnabled);
+            mInjector.runCryptoSelfTest();
         } else {
             synchronized (getLockObject()) {
                 mSecurityLogMonitor.start(getSecurityLoggingEnabledUser());
diff --git a/services/people/java/com/android/server/people/TEST_MAPPING b/services/people/java/com/android/server/people/TEST_MAPPING
new file mode 100644
index 0000000..55b355c
--- /dev/null
+++ b/services/people/java/com/android/server/people/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+    "presubmit": [
+        {
+            "name": "FrameworksServicesTests",
+            "options": [
+                {
+                    "include-filter": "com.android.server.people.data"
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
index 9e11fa2..e545a49 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
@@ -71,7 +71,7 @@
     @Mock
     private Installer mInstaller;
 
-    private Object mInstallLock;
+    private PackageManagerTracedLock mInstallLock;
 
     @Before
     public void setup() {
@@ -79,7 +79,7 @@
         TEST_USER.serialNumber = TEST_USER_SERIAL;
         Context ctx = InstrumentationRegistry.getContext();
         FileUtils.deleteContents(ctx.getCacheDir());
-        mInstallLock = new Object();
+        mInstallLock = new PackageManagerTracedLock();
         MockitoAnnotations.initMocks(this);
         mUserDataPreparer = new TestUserDataPreparer(mInstaller, mInstallLock, mContextMock,
                 ctx.getCacheDir());
@@ -238,8 +238,8 @@
     private static class TestUserDataPreparer extends UserDataPreparer {
         File testDir;
 
-        TestUserDataPreparer(Installer installer, Object installLock, Context context,
-                File testDir) {
+        TestUserDataPreparer(Installer installer, PackageManagerTracedLock installLock,
+                Context context, File testDir) {
             super(installer, installLock, context);
             this.testDir = testDir;
         }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index ae6361b..df96712 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -46,6 +46,7 @@
 import com.android.server.display.brightness.strategy.BoostBrightnessStrategy;
 import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
 import com.android.server.display.brightness.strategy.DozeBrightnessStrategy;
+import com.android.server.display.brightness.strategy.FallbackBrightnessStrategy;
 import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy;
 import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy;
 import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy;
@@ -90,6 +91,8 @@
     @Mock
     private AutoBrightnessFallbackStrategy mAutoBrightnessFallbackStrategy;
     @Mock
+    private FallbackBrightnessStrategy mFallbackBrightnessStrategy;
+    @Mock
     private Resources mResources;
     @Mock
     private DisplayManagerFlags mDisplayManagerFlags;
@@ -135,7 +138,7 @@
 
                 @Override
                 AutomaticBrightnessStrategy getAutomaticBrightnessStrategy1(Context context,
-                        int displayId) {
+                        int displayId, DisplayManagerFlags displayManagerFlags) {
                     return mAutomaticBrightnessStrategy;
                 }
 
@@ -155,6 +158,11 @@
                 AutoBrightnessFallbackStrategy getAutoBrightnessFallbackStrategy() {
                     return mAutoBrightnessFallbackStrategy;
                 }
+
+                @Override
+                FallbackBrightnessStrategy getFallbackBrightnessStrategy() {
+                    return mFallbackBrightnessStrategy;
+                }
             };
 
     @Rule
@@ -355,6 +363,25 @@
     }
 
     @Test
+    public void selectStrategy_selectsFallbackStrategyAsAnUltimateFallback() {
+        when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
+        mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
+                mInjector, DISPLAY_ID, mDisplayManagerFlags);
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+                DisplayManagerInternal.DisplayPowerRequest.class);
+        displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+        displayPowerRequest.screenBrightnessOverride = Float.NaN;
+        when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
+        when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
+        when(mAutomaticBrightnessStrategy.shouldUseAutoBrightness()).thenReturn(false);
+        when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(false);
+        assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
+                        new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
+                                0.1f, false)),
+                mFallbackBrightnessStrategy);
+    }
+
+    @Test
     public void selectStrategyCallsPostProcessorForAllStrategies() {
         when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
         mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index 3e78118..19bff56 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -18,8 +18,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -45,6 +47,7 @@
 import com.android.server.display.brightness.BrightnessEvent;
 import com.android.server.display.brightness.BrightnessReason;
 import com.android.server.display.brightness.StrategyExecutionRequest;
+import com.android.server.display.feature.DisplayManagerFlags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -64,6 +67,9 @@
     @Mock
     private AutomaticBrightnessController mAutomaticBrightnessController;
 
+    @Mock
+    private DisplayManagerFlags mDisplayManagerFlags;
+
     private BrightnessConfiguration mBrightnessConfiguration;
     private float mDefaultScreenAutoBrightnessAdjustment;
     private Context mContext;
@@ -80,7 +86,8 @@
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, Float.NaN);
         Settings.System.putFloat(mContext.getContentResolver(),
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.5f);
-        mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, DISPLAY_ID);
+        mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, DISPLAY_ID,
+                mDisplayManagerFlags);
 
         mBrightnessConfiguration = new BrightnessConfiguration.Builder(
                 new float[]{0f, 1f}, new float[]{0, PowerManager.BRIGHTNESS_ON}).build();
@@ -247,6 +254,46 @@
     }
 
     @Test
+    public void testAutoBrightnessState_modeSwitch() {
+        // Setup the test
+        when(mDisplayManagerFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
+        mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
+        boolean allowAutoBrightnessWhileDozing = false;
+        int brightnessReason = BrightnessReason.REASON_UNKNOWN;
+        float lastUserSetBrightness = 0.2f;
+        boolean userSetBrightnessChanged = true;
+        int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+        float pendingBrightnessAdjustment = 0.1f;
+        Settings.System.putFloat(mContext.getContentResolver(),
+                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
+        mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
+
+        // Validate no interaction when automaticBrightnessController is in idle mode
+        when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(true);
+        mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
+                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+                userSetBrightnessChanged);
+        verify(mAutomaticBrightnessController, never()).switchMode(anyInt());
+
+        // Validate interaction when automaticBrightnessController is in non-idle mode, and display
+        // state is ON
+        when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(false);
+        mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
+                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+                userSetBrightnessChanged);
+        verify(mAutomaticBrightnessController).switchMode(
+                AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT);
+
+        // Validate interaction when automaticBrightnessController is in non-idle mode, and display
+        // state is DOZE
+        mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_DOZE,
+                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+                userSetBrightnessChanged);
+        verify(mAutomaticBrightnessController).switchMode(
+                AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE);
+    }
+
+    @Test
     public void accommodateUserBrightnessChangesWorksAsExpected() {
         // Verify the state if automaticBrightnessController is configured.
         assertFalse(mAutomaticBrightnessStrategy.isShortTermModelActive());
@@ -390,7 +437,8 @@
     @Test
     public void testVerifyNoAutoBrightnessAdjustmentsArePopulatedForNonDefaultDisplay() {
         int newDisplayId = 1;
-        mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, newDisplayId);
+        mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, newDisplayId,
+                mDisplayManagerFlags);
         mAutomaticBrightnessStrategy.putAutoBrightnessAdjustmentSetting(0.3f);
         assertEquals(0.5f, mAutomaticBrightnessStrategy.getAutoBrightnessAdjustment(),
                 0.0f);
@@ -429,8 +477,7 @@
             updateBrightness_constructsDisplayBrightnessState_withAdjustmentAutoAdjustmentFlag() {
         BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID);
         mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(
-                mContext, DISPLAY_ID, displayId -> brightnessEvent);
-        new AutomaticBrightnessStrategy(mContext, DISPLAY_ID);
+                mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags);
         mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
                 mAutomaticBrightnessController);
         float brightness = 0.4f;
@@ -461,8 +508,7 @@
             updateBrightness_constructsDisplayBrightnessState_withAdjustmentTempAdjustmentFlag() {
         BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID);
         mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(
-                mContext, DISPLAY_ID, displayId -> brightnessEvent);
-        new AutomaticBrightnessStrategy(mContext, DISPLAY_ID);
+                mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags);
         mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
                 mAutomaticBrightnessController);
         float brightness = 0.4f;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
new file mode 100644
index 0000000..c4767ae
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.strategy;
+
+
+import static org.junit.Assert.assertEquals;
+
+import android.hardware.display.DisplayManagerInternal;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.StrategyExecutionRequest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+
+public class FallbackBrightnessStrategyTest {
+    private FallbackBrightnessStrategy mFallbackBrightnessStrategy;
+
+    @Before
+    public void before() {
+        mFallbackBrightnessStrategy = new FallbackBrightnessStrategy();
+    }
+
+    @Test
+    public void updateBrightness_currentBrightnessIsSet() {
+        DisplayManagerInternal.DisplayPowerRequest
+                displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest();
+        float currentBrightness = 0.2f;
+        BrightnessReason brightnessReason = new BrightnessReason();
+        brightnessReason.setReason(BrightnessReason.REASON_MANUAL);
+        DisplayBrightnessState expectedDisplayBrightnessState =
+                new DisplayBrightnessState.Builder()
+                        .setBrightness(currentBrightness)
+                        .setBrightnessReason(brightnessReason)
+                        .setSdrBrightness(currentBrightness)
+                        .setDisplayBrightnessStrategyName(mFallbackBrightnessStrategy.getName())
+                        .setShouldUpdateScreenBrightnessSetting(true)
+                        .build();
+        DisplayBrightnessState updatedDisplayBrightnessState =
+                mFallbackBrightnessStrategy.updateBrightness(
+                        new StrategyExecutionRequest(displayPowerRequest, currentBrightness));
+        assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
+    }
+}
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
index 88ab871..874e991 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
@@ -273,28 +273,36 @@
     }
 
     @Test
-    public void setDreamHasFocus_true_dreamHasFocus() {
+    public void setDreamIsObscured_true_dreamIsNotFrontmost() {
         mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
                 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
 
-        mDreamController.setDreamHasFocus(true);
-        assertTrue(mDreamController.dreamHasFocus());
+        mDreamController.setDreamIsObscured(true);
+        assertFalse(mDreamController.dreamIsFrontmost());
     }
 
     @Test
-    public void setDreamHasFocus_false_dreamDoesNotHaveFocus() {
+    public void setDreamIsObscured_false_dreamIsFrontmost() {
         mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
                 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
 
-        mDreamController.setDreamHasFocus(false);
-        assertFalse(mDreamController.dreamHasFocus());
+        mDreamController.setDreamIsObscured(false);
+        assertTrue(mDreamController.dreamIsFrontmost());
     }
 
     @Test
-    public void setDreamHasFocus_notDreaming_dreamDoesNotHaveFocus() {
-        mDreamController.setDreamHasFocus(true);
-        // Dream still doesn't have focus because it was never started.
-        assertFalse(mDreamController.dreamHasFocus());
+    public void setDreamIsObscured_notDreaming_dreamIsNotFrontmost() {
+        mDreamController.setDreamIsObscured(true);
+        // Dream still isn't frontmost because it was never started.
+        assertFalse(mDreamController.dreamIsFrontmost());
+    }
+
+    @Test
+    public void startDream_dreamIsFrontmost() {
+        mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
+                0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
+
+        assertTrue(mDreamController.dreamIsFrontmost());
     }
 
     private ServiceConnection captureServiceConnection() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index c9aab53..396edae 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -186,7 +186,7 @@
 
     class Mocks {
         val lock = PackageManagerTracedLock()
-        val installLock = Any()
+        val installLock = PackageManagerTracedLock()
         val injector: PackageManagerServiceInjector = mock()
         val systemWrapper: PackageManagerServiceInjector.SystemWrapper = mock()
         val context: Context = mock()
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 9fc46c5..2f3bca0 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -1786,10 +1786,10 @@
                     /* matchesInterruptionFilter= */ false,
                     /* visibilityOverride= */ 0,
                     /* suppressedVisualEffects= */ 0,
-                    mParentNotificationChannel.getImportance(),
+                    mNotificationChannel.getImportance(),
                     /* explanation= */ null,
                     /* overrideGroupKey= */ null,
-                    mParentNotificationChannel,
+                    mNotificationChannel,
                     /* overridePeople= */ null,
                     /* snoozeCriteria= */ null,
                     /* showBadge= */ true,
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index a60d243..1195c93 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -1623,6 +1623,12 @@
         assertTrue(mController.allowHorizontalReachabilityForThinLetterbox());
     }
 
+    @Test
+    public void testIsLetterboxEducationEnabled() {
+        mController.isLetterboxEducationEnabled();
+        verify(mLetterboxConfiguration).getIsEducationEnabled();
+    }
+
     private void mockThatProperty(String propertyName, boolean value) throws Exception {
         Property property = new Property(propertyName, /* value */ value, /* packageName */ "",
                 /* className */ "");
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
index 2b6ddcb..da8368f 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
@@ -32,9 +32,6 @@
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
-/**
- * To run this test: `atest FlickerTestsIme1:CloseImeOnDismissPopupDialogTest`
- */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
index 0344197..2f3ec63 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
@@ -33,8 +33,8 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME window closing to home transitions.
- * To run this test: `atest FlickerTestsIme1:CloseImeOnGoHomeTest`
+ * Test IME window closing to home transitions. To run this test: `atest
+ * FlickerTests:CloseImeWindowToHomeTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
index fde1373..8821b69 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
@@ -42,7 +42,7 @@
  *
  * More details on b/190352379
  *
- * To run this test: `atest FlickerTestsIme1:CloseImeShownOnAppStartOnGoHomeTest`
+ * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
index dc50135..d75eba6 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
@@ -42,7 +42,7 @@
  *
  * More details on b/190352379
  *
- * To run this test: `atest FlickerTestsIme1:CloseImeShownOnAppStartToAppOnPressBackTest`
+ * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
index dc2bd1b..41d9e30 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -34,8 +34,8 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME window closing back to app window transitions.
- * To run this test: `atest FlickerTestsIme1:CloseImeToAppOnPressBackTest`
+ * Test IME window closing back to app window transitions. To run this test: `atest
+ * FlickerTests:CloseImeWindowToAppTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
index 05771e8..0e7fb79 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
@@ -40,7 +40,7 @@
  * Unlike {@link OpenImeWindowTest} testing IME window opening transitions, this test also verify
  * there is no flickering when back to the simple activity without requesting IME to show.
  *
- * To run this test: `atest FlickerTestsIme1:CloseImeToHomeOnFinishActivityTest`
+ * To run this test: `atest FlickerTests:OpenImeWindowAndCloseTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
index 336fe6f..47a7e1b 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
@@ -36,8 +36,8 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME window shown on the app with fixing portrait orientation.
- * To run this test: `atest FlickerTestsIme2:OpenImeWindowToFixedPortraitAppTest`
+ * Test IME window shown on the app with fixing portrait orientation. To run this test: `atest
+ * FlickerTests:OpenImeWindowToFixedPortraitAppTest`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index b8f11dc..48ec4d1 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -38,9 +38,8 @@
 
 /**
  * Test IME window layer will become visible when switching from the fixed orientation activity
- * (e.g. Launcher activity).
- * To run this test:
- * `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest`
+ * (e.g. Launcher activity). To run this test: `atest
+ * FlickerTests:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
index 34a7085..03f3a68 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
@@ -33,8 +33,7 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME window opening transitions.
- * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromOverviewTest`
+ * Test IME window opening transitions. To run this test: `atest FlickerTests:ReOpenImeWindowTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
index 7c72c31..7b62c89 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
@@ -35,8 +35,8 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME windows switching with 2-Buttons or gestural navigation.
- * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest`
+ * Test IME windows switching with 2-Buttons or gestural navigation. To run this test: `atest
+ * FlickerTests:SwitchImeWindowsFromGestureNavTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
index fe5320c..53bfb4e 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
@@ -36,7 +36,7 @@
 /**
  * Launch an app that automatically displays the IME
  *
- * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppTest`
+ * To run this test: `atest FlickerTests:LaunchAppShowImeOnStartTest`
  *
  * Actions:
  * ```
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
index 92b6b93..d22bdcf 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
@@ -35,8 +35,8 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME window closing on lock and opening on screen unlock.
- * To run this test: `atest FlickerTestsIme2:ShowImeOnUnlockScreenTest`
+ * Test IME window closing on lock and opening on screen unlock. To run this test: `atest
+ * FlickerTests:CloseImeWindowToHomeTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
index 9eaf998..12290af 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
@@ -31,10 +31,7 @@
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
-/**
- * Test IME window opening transitions.
- * To run this test: `atest FlickerTestsIme2:ShowImeWhenFocusingOnInputFieldTest`
- */
+/** Test IME window opening transitions. To run this test: `atest FlickerTests:OpenImeWindowTest` */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
index 7186a2c..0948351 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
@@ -41,7 +41,7 @@
 
 /**
  * Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity.
- * To run this test: `atest FlickerTestsIme2:ShowImeWhileDismissingThemedPopupDialogTest`
+ * To run this test: `atest FlickerTests:LaunchAppShowImeAndDialogThemeAppTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index c96c760..7aa525f 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -37,8 +37,8 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test IME window layer will be associated with the app task when going to the overview screen.
- * To run this test: `atest FlickerTestsIme2:ShowImeWhileEnteringOverviewTest`
+ * Test IME window layer will be associated with the app task when going to the overview screen. To
+ * run this test: `atest FlickerTests:OpenImeWindowToOverViewTest`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
index 8c9ab9a..ffaeead 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
@@ -40,7 +40,7 @@
  *
  * This test assumes the device doesn't have AOD enabled
  *
- * To run this test: `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationColdTest`
+ * To run this test: `atest FlickerTests:OpenAppFromLockNotificationCold`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
index e595100a..6e67e19 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
@@ -40,7 +40,7 @@
  *
  * This test assumes the device doesn't have AOD enabled
  *
- * To run this test: `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationWarmTest`
+ * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWarm`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
index fbe1d34..f1df8a6 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
@@ -40,8 +40,7 @@
  *
  * This test assumes the device doesn't have AOD enabled
  *
- * To run this test:
- * `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationWithOverlayAppTest`
+ * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWithLockOverlayApp`
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
index c8ca644..b6d09d0 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
@@ -36,7 +36,7 @@
 /**
  * Test cold launching an app from a notification.
  *
- * To run this test: `atest FlickerTestsNotification:OpenAppFromNotificationColdTest`
+ * To run this test: `atest FlickerTests:OpenAppFromNotificationCold`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
index c29e71c..1e607bf 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
@@ -47,7 +47,7 @@
 /**
  * Test cold launching an app from a notification.
  *
- * To run this test: `atest FlickerTestsNotification:OpenAppFromNotificationWarmTest`
+ * To run this test: `atest FlickerTests:OpenAppFromNotificationWarm`
  */
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 96d4e02..9198ae1 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -17,10 +17,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.server.wm.flicker.testapp">
 
-    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
-
     <uses-sdk android:minSdkVersion="29"
-              android:targetSdkVersion="35"/>
+              android:targetSdkVersion="29"/>
     <application android:allowBackup="false"
                  android:supportsRtl="true">
         <uses-library android:name="androidx.window.extensions" android:required="false"/>
@@ -109,7 +107,7 @@
                   android:immersive="true"
                   android:resizeableActivity="true"
                   android:screenOrientation="portrait"
-                  android:theme="@style/OptOutEdgeToEdge.NoTitleBar"
+                  android:theme="@android:style/Theme.NoTitleBar"
                   android:configChanges="screenSize"
                   android:label="PortraitImmersiveActivity"
                   android:exported="true">
@@ -121,7 +119,7 @@
         <activity android:name=".LaunchTransparentActivity"
                   android:resizeableActivity="false"
                   android:screenOrientation="portrait"
-                  android:theme="@style/OptOutEdgeToEdge"
+                  android:theme="@android:style/Theme"
                   android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchTransparentActivity"
                   android:label="LaunchTransparentActivity"
                   android:exported="true">
@@ -284,7 +282,7 @@
         <activity android:name=".GameActivity"
                   android:taskAffinity="com.android.server.wm.flicker.testapp.GameActivity"
                   android:immersive="true"
-                  android:theme="@style/OptOutEdgeToEdge.NoTitleBar"
+                  android:theme="@android:style/Theme.NoTitleBar"
                   android:configChanges="screenSize"
                   android:label="GameActivity"
                   android:exported="true">
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 120077c..9b742d9 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -16,15 +16,7 @@
   -->
 
 <resources>
-    <style name="OptOutEdgeToEdge" parent="@android:style/Theme.DeviceDefault">
-        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
-    </style>
-
-    <style name="OptOutEdgeToEdge.NoTitleBar" parent="@android:style/Theme.NoTitleBar">
-        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
-    </style>
-
-    <style name="DefaultTheme" parent="@style/OptOutEdgeToEdge">
+    <style name="DefaultTheme" parent="@android:style/Theme.DeviceDefault">
         <item name="android:windowBackground">@android:color/darker_gray</item>
     </style>
 
@@ -40,7 +32,7 @@
         <item name="android:windowLayoutInDisplayCutoutMode">never</item>
     </style>
 
-    <style name="DialogTheme" parent="@style/OptOutEdgeToEdge">
+    <style name="DialogTheme" parent="@android:style/Theme.DeviceDefault">
         <item name="android:windowAnimationStyle">@null</item>
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">@null</item>
@@ -51,18 +43,18 @@
         <item name="android:windowSoftInputMode">stateUnchanged</item>
     </style>
 
-    <style name="TransparentTheme" parent="@style/OptOutEdgeToEdge">
+    <style name="TransparentTheme" parent="@android:style/Theme.DeviceDefault">
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowContentOverlay">@null</item>
         <item name="android:backgroundDimEnabled">false</item>
     </style>
 
-    <style name="no_starting_window" parent="@style/OptOutEdgeToEdge">
+    <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault">
         <item name="android:windowDisablePreview">true</item>
     </style>
 
-    <style name="SplashscreenAppTheme" parent="@style/OptOutEdgeToEdge">
+    <style name="SplashscreenAppTheme" parent="@android:style/Theme.DeviceDefault">
         <!-- Splashscreen Attributes -->
         <item name="android:windowSplashScreenAnimatedIcon">@drawable/avd_anim</item>
         <!-- Here we want to match the duration of our AVD -->
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
index a86ba5f..c92b82b 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
@@ -125,7 +125,7 @@
                 .setContentTitle("BubbleChat")
                 .setContentIntent(PendingIntent.getActivity(mContext, 0,
                         new Intent(mContext, LaunchBubbleActivity.class),
-                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE))
+                        PendingIntent.FLAG_UPDATE_CURRENT))
                 .setStyle(new Notification.MessagingStyle(chatBot)
                         .setConversationTitle("BubbleChat")
                         .addMessage("BubbleChat",
@@ -140,7 +140,7 @@
         Intent target = new Intent(mContext, BubbleActivity.class);
         target.putExtra(EXTRA_BUBBLE_NOTIF_ID, info.id);
         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, info.id, target,
-                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
+                PendingIntent.FLAG_UPDATE_CURRENT);
 
         return new Notification.BubbleMetadata.Builder()
                 .setIntent(bubbleIntent)
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
index f788810..dea3444 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
@@ -17,9 +17,6 @@
 package com.android.server.wm.flicker.testapp;
 
 
-import static android.Manifest.permission.POST_NOTIFICATIONS;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
 import android.app.Activity;
 import android.app.Person;
 import android.content.Context;
@@ -27,7 +24,6 @@
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
 import android.graphics.drawable.Icon;
-import android.os.Build;
 import android.os.Bundle;
 import android.view.View;
 
@@ -40,13 +36,6 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
-                && checkSelfPermission(POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
-            // POST_NOTIFICATIONS permission required for Bubble post sdk 33.
-            requestPermissions(new String[] { POST_NOTIFICATIONS }, 0);
-        }
-
         addInboxShortcut(getApplicationContext());
         mBubbleHelper = BubbleHelper.getInstance(this);
         setContentView(R.layout.activity_main);
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
index d6427ab..a4dd575 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
@@ -16,9 +16,6 @@
 
 package com.android.server.wm.flicker.testapp;
 
-import static android.Manifest.permission.POST_NOTIFICATIONS;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
 import android.app.Activity;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -26,7 +23,6 @@
 import android.app.PendingIntent;
 import android.app.TaskStackBuilder;
 import android.content.Intent;
-import android.os.Build;
 import android.os.Bundle;
 import android.view.WindowManager;
 import android.widget.Button;
@@ -38,13 +34,6 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
-                && checkSelfPermission(POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
-            // POST_NOTIFICATIONS permission required for notification post sdk 33.
-            requestPermissions(new String[] { POST_NOTIFICATIONS }, 0);
-        }
-
         WindowManager.LayoutParams p = getWindow().getAttributes();
         p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
                 .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
index 27eb5a0..1ab8ddb 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -198,7 +198,7 @@
         filter.addAction(ACTION_SET_REQUESTED_ORIENTATION);
         filter.addAction(ACTION_ENTER_PIP);
         filter.addAction(ACTION_ASPECT_RATIO);
-        registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED);
+        registerReceiver(mBroadcastReceiver, filter);
 
         handleIntentExtra(getIntent());
     }
@@ -222,8 +222,8 @@
 
     private RemoteAction buildRemoteAction(Icon icon, String label, String action) {
         final Intent intent = new Intent(action);
-        final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent,
-                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+        final PendingIntent pendingIntent =
+                PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
         return new RemoteAction(icon, label, label, pendingIntent);
     }
 
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
index 16785d1..6b360b7 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
@@ -25,6 +25,7 @@
         val aidlPolicy: FilterPolicyWithReason?,
         val featureFlagsPolicy: FilterPolicyWithReason?,
         val syspropsPolicy: FilterPolicyWithReason?,
+        val rFilePolicy: FilterPolicyWithReason?,
         fallback: OutputFilter
 ) : DelegatingFilter(fallback) {
     override fun getPolicyForClass(className: String): FilterPolicyWithReason {
@@ -37,6 +38,9 @@
         if (syspropsPolicy != null && classes.isSyspropsClass(className)) {
             return syspropsPolicy
         }
+        if (rFilePolicy != null && classes.isRClass(className)) {
+            return rFilePolicy
+        }
         return super.getPolicyForClass(className)
     }
 }
@@ -74,3 +78,10 @@
     return className.startsWith("android/sysprop/")
             && className.endsWith("Properties")
 }
+
+/**
+ * @return if a given class "seems like" an R class or its nested classes.
+ */
+private fun ClassNodes.isRClass(className: String): Boolean {
+    return className.endsWith("/R") || className.contains("/R$")
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index 75b5fc8..c5acd81 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -17,6 +17,7 @@
 
 import com.android.hoststubgen.ParseException
 import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.toHumanReadableClassName
 import com.android.hoststubgen.log
 import com.android.hoststubgen.normalizeTextLine
 import com.android.hoststubgen.whitespaceRegex
@@ -31,13 +32,17 @@
  * Print a class node as a "keep" policy.
  */
 fun printAsTextPolicy(pw: PrintWriter, cn: ClassNode) {
-    pw.printf("class %s\t%s\n", cn.name, "keep")
+    pw.printf("class %s %s\n", cn.name.toHumanReadableClassName(), "keep")
 
-    for (f in cn.fields ?: emptyList()) {
-        pw.printf("  field %s\t%s\n", f.name, "keep")
+    cn.fields?.let {
+        for (f in it.sortedWith(compareBy({ it.name }))) {
+            pw.printf("    field %s %s\n", f.name, "keep")
+        }
     }
-    for (m in cn.methods ?: emptyList()) {
-        pw.printf("  method %s\t%s\t%s\n", m.name, m.desc, "keep")
+    cn.methods?.let {
+        for (m in it.sortedWith(compareBy({ it.name }, { it.desc }))) {
+            pw.printf("    method %s %s %s\n", m.name, m.desc, "keep")
+        }
     }
 }
 
@@ -66,6 +71,7 @@
         var aidlPolicy: FilterPolicyWithReason? = null
         var featureFlagsPolicy: FilterPolicyWithReason? = null
         var syspropsPolicy: FilterPolicyWithReason? = null
+        var rFilePolicy: FilterPolicyWithReason? = null
 
         try {
             BufferedReader(FileReader(filename)).use { reader ->
@@ -162,6 +168,14 @@
                                         syspropsPolicy = policy.withReason(
                                                 "$FILTER_REASON (special-class sysprops)")
                                     }
+                                    SpecialClass.RFile -> {
+                                        if (rFilePolicy != null) {
+                                            throw ParseException(
+                                                "Policy for R file already defined")
+                                        }
+                                        rFilePolicy = policy.withReason(
+                                            "$FILTER_REASON (special-class R file)")
+                                    }
                                 }
                             }
                         }
@@ -225,13 +239,9 @@
             throw e.withSourceInfo(filename, lineNo)
         }
 
-        var ret: OutputFilter = imf
-        if (aidlPolicy != null || featureFlagsPolicy != null || syspropsPolicy != null) {
-            log.d("AndroidHeuristicsFilter enabled")
-            ret = AndroidHeuristicsFilter(
-                    classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, imf)
-        }
-        return ret
+        // Wrap the in-memory-filter with AHF.
+        return AndroidHeuristicsFilter(
+                classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, rFilePolicy, imf)
     }
 }
 
@@ -240,6 +250,7 @@
     Aidl,
     FeatureFlags,
     Sysprops,
+    RFile,
 }
 
 private fun resolveSpecialClass(className: String): SpecialClass {
@@ -250,6 +261,7 @@
         ":aidl" -> return SpecialClass.Aidl
         ":feature_flags" -> return SpecialClass.FeatureFlags
         ":sysprops" -> return SpecialClass.Sysprops
+        ":r" -> return SpecialClass.RFile
     }
     throw ParseException("Invalid special class name \"$className\"")
 }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index fa8fe6c..931f0c5 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -322,6 +322,78 @@
 InnerClasses:
   public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
   public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 3
+  public static int[] ARRAY;
+    descriptor: [I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+
+  public com.android.hoststubgen.test.tinyframework.R$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: iconst_1
+         x: newarray       int
+         x: dup
+         x: iconst_0
+         x: iconst_1
+         x: iastore
+         x: putstatic     #x                  // Field ARRAY:[I
+        x: return
+      LineNumberTable:
+}
+SourceFile: "R.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+InnerClasses:
+  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 3
+  public com.android.hoststubgen.test.tinyframework.R();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R;
+}
+SourceFile: "R.java"
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/R$Nested
+InnerClasses:
+  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index c605f76..906a81c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -122,6 +122,100 @@
 NestMembers:
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  public static int[] ARRAY;
+    descriptor: [I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  public com.android.hoststubgen.test.tinyframework.R$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 4
+  public com.android.hoststubgen.test.tinyframework.R();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/R$Nested
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 11d5939..10bc91d 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -348,6 +348,108 @@
 NestMembers:
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  public static int[] ARRAY;
+    descriptor: [I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  public com.android.hoststubgen.test.tinyframework.R$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: iconst_1
+         x: newarray       int
+         x: dup
+         x: iconst_0
+         x: iconst_1
+         x: iastore
+         x: putstatic     #x                 // Field ARRAY:[I
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 4
+  public com.android.hoststubgen.test.tinyframework.R();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/R$Nested
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index c605f76..906a81c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -122,6 +122,100 @@
 NestMembers:
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  public static int[] ARRAY;
+    descriptor: [I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  public com.android.hoststubgen.test.tinyframework.R$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 4
+  public com.android.hoststubgen.test.tinyframework.R();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/R$Nested
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index 088bc80..fcf9a8c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -481,6 +481,136 @@
 NestMembers:
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  public static int[] ARRAY;
+    descriptor: [I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  public com.android.hoststubgen.test.tinyframework.R$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R$Nested
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R$Nested
+         x: ldc           #x                 // String <clinit>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R$Nested
+        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+        x: iconst_1
+        x: newarray       int
+        x: dup
+        x: iconst_0
+        x: iconst_1
+        x: iastore
+        x: putstatic     #x                 // Field ARRAY:[I
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 2, attributes: 4
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.R();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/R$Nested
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index d302084..696b6d0 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -19,6 +19,9 @@
 # Heuristics rule: Stub all the AIDL classes.
 class :aidl stubclass
 
+# Heuristics rule: Stub all the R classes.
+class :r stubclass
+
 # Default is "remove", so let's put all the base classes / interfaces in the stub first.
 class com.android.hoststubgen.test.tinyframework.subclasstest.C1 stub
 class com.android.hoststubgen.test.tinyframework.subclasstest.C2 stub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
new file mode 100644
index 0000000..b1bedf4
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework;
+
+public class R {
+    public static class Nested {
+        public static int[] ARRAY = new int[] {1};
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 762180d..37925e8 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -19,6 +19,7 @@
 
 import static org.junit.Assert.fail;
 
+import com.android.hoststubgen.test.tinyframework.R.Nested;
 import com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.SubClass;
 
 import org.junit.Rule;
@@ -328,4 +329,9 @@
         assertThat(IPretendingAidl.Stub.addOne(1)).isEqualTo(2);
         assertThat(IPretendingAidl.Stub.Proxy.addTwo(1)).isEqualTo(3);
     }
+
+    @Test
+    public void testRFileHeuristics() {
+        assertThat(Nested.ARRAY.length).isEqualTo(1);
+    }
 }